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 001/846] 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 cb44eccae684..f16b98883b80 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 002/846] 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 f16b98883b80..6b5a8498dc92 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 3dbb2155d4de..ec80f6070d62 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 003/846] 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 6b5a8498dc92..ca61c97e3e3f 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 aa347288875d..1f41838fa169 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 ec80f6070d62..95114c46f368 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 004/846] 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 ca61c97e3e3f..502bd42214ee 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 005/846] 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 502bd42214ee..3dd3a79b2873 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 006/846] 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 3dd3a79b2873..321d5265d0c2 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 007/846] 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 321d5265d0c2..e37c44dc026a 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 008/846] 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 e37c44dc026a..2dc95f53078e 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 009/846] 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 2dc95f53078e..0753b23e45b3 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 010/846] 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 0753b23e45b3..75955997af24 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 011/846] 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 75955997af24..8ab355566706 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 012/846] 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 8ab355566706..7cf3e16bef9b 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 013/846] 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 7cf3e16bef9b..5f7f08979436 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 014/846] 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 adfd8dfb1c18..62ee051624b4 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 015/846] 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 1f41838fa169..9c24d6d4db1f 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 95114c46f368..bad84a589008 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 016/846] 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 575773580c0b..facf363e371e 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 c995be5edc25..64f3a8a0ebbb 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 000000000000..ca2a2cf2e1e7 --- /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 72675c25175c..c5360002fa05 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 000000000000..9c239db6e00d --- /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 000000000000..d6719bca0d4c --- /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 017/846] 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 ca2a2cf2e1e7..a4273da1d741 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 9c239db6e00d..7a788f485207 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 d6719bca0d4c..613c5e8ca1d4 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 018/846] 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 c995be5edc25..06e21a5272ec 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 4071406cc84c..421571d2f2f4 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 72675c25175c..f337db72ba00 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 019/846] 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 a4273da1d741..f3b8902e26f6 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 020/846] 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 5235c98efab1..f3844c7d3b68 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 b0fc26ff76de..3e7b4b390bad 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 e40542e2e4f9..4013d1ed667f 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 021/846] 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 04b4b4237619..1c7b40fa9087 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 f5fcabf63fd3..e99c98ac79f2 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 04dce8a02898..ba13172428e1 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 6de138d7227b..aa980d9965c7 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 022/846] 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 421571d2f2f4..236362130e54 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 0bb39d77e461..30415e3b94dc 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 023/846] 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 236362130e54..ee69628e9f05 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 ca2c4ade1556..79ca6845c6f2 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 024/846] 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 50a5ee8bbf3c..079a51eae3ba 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 025/846] 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 079a51eae3ba..f7a609383744 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 026/846] 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 d5cace0c6474..d563eb886ae7 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 027/846] 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 b6a4adde8488..bc785b075e02 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 028/846] 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 facf363e371e..8457d6ad05cf 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 97785cba8835..fb2e9932b8e4 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 000000000000..ab8829abbf2e --- /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 79ca6845c6f2..74040a96c78a 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 a7685f67211f..51d1cb2216a9 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 6d09d71a630a..d3a920de4b6a 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 3b4968ef0a63..d1863701bfe7 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 000000000000..84c02bba4dcb --- /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 000000000000..bd5f9d1cf5f2 --- /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 000000000000..016fdf959772 --- /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 029/846] 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 ab8829abbf2e..b4be3ecfd162 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 030/846] 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 b4be3ecfd162..cb72a2405823 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 74040a96c78a..b3ad2ad9d998 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 84c02bba4dcb..6bb1032a1729 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 bd5f9d1cf5f2..d50c919188be 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 016fdf959772..f278ee41aa33 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 031/846] 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 63ce2cd8cad7..6999b6bd7404 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 032/846] 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 f3038861cee2..70ad9a60a02c 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 ed6cc00ef048..003f11009a06 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 c6d6efaece81..7bd57c7965a6 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 050e4c6304f0..858dc28797fa 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 033/846] 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 70ad9a60a02c..e7400642b078 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 034/846] 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 e7400642b078..a22d0e6775de 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 003f11009a06..1c5c01f65d15 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 7bd57c7965a6..f783149ef93e 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 858dc28797fa..649d550a88d1 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 035/846] 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 fb2e9932b8e4..4b67c84e38ed 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 4b81ff33495c..57b9eafd14db 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 a22d0e6775de..43d0d1b9e96c 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 18f5d994ba8a..53970af41079 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 1c5c01f65d15..98bf6acfe55f 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 f783149ef93e..9275c5a09286 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 649d550a88d1..bd000ce81616 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 036/846] 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 43d0d1b9e96c..843ddda03569 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 037/846] 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 843ddda03569..48405a00d554 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 038/846] 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 48405a00d554..e12a6659ab5b 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 98bf6acfe55f..b47c8f23e240 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 9275c5a09286..3ad1a29aebad 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 bd000ce81616..de07bd1d69ba 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 039/846] 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 b47c8f23e240..67423e6ec1d1 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 040/846] 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 e12a6659ab5b..2c4e24780e7d 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 de07bd1d69ba..fab43b738eb4 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 041/846] 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 2c4e24780e7d..32d9a45c37d7 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 042/846] 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 f7a91fcdd213..8d75e1269630 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 043/846] 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 000000000000..319de217e0d9 --- /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 044/846] 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 91d3e47d7870..278d043732f4 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 1ebfb3c8162a..475610dda475 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 1d86ca9696f2..2ec0b5a8d6fb 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 4b67c84e38ed..51b5401da7d0 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 0b346393ac38..0163b3f8dbc8 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 45809b359866..2b51b7320758 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 86c469a4dccf..735800e7e741 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 02b721fd378f..bd7da57c665d 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 5bf9acdc5f7c..8e1b047f6f80 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 1c7b40fa9087..3b8ef18bfab8 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 240bf2449cb5..fb6bd5e81585 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 c14da6aacea0..e85356779877 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 045/846] 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 319de217e0d9..000000000000 --- 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 046/846] 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 278d043732f4..90c00ad098ff 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 0163b3f8dbc8..d5adf6b0f0dc 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 e85356779877..904d948ad29e 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 047/846] 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 e0b5b31a00c7..859765d08a70 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 39d816089229..24cb216e79bf 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 048/846] 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 5c9117d5b81c..35464f629c36 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 3f63624720f7..07f2791786d7 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 8fc072ebd69f..e2c6afb020f5 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 a013d2b3495b..81e6738e6bf6 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 049/846] 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 8457d6ad05cf..33b277fbd318 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 51b5401da7d0..e1cb10a46512 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 d7ce2e66d69f..86d55ccabb60 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 000000000000..ee2cbc3cf540 --- /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 000000000000..6ed5ca6daa0e --- /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 000000000000..97933b8ff851 --- /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 000000000000..c9ca4c476683 --- /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 000000000000..12e5483ecdff --- /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 050/846] 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 33b277fbd318..b25ef0493568 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 e1cb10a46512..0c4daeb731f9 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 2bbf4dba614b..0bc6b70855ba 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 51d1cb2216a9..e1a6d4bdd31f 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 5fc84ada9efd..249a88a0b398 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 4165b0dc0049..306d85a6351e 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 cffb4b9f0a9c..ddfe66d675f9 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 179b255e08ca..e73536052f0f 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 1da8e0f3588d..1c9287b6acbb 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 9c24d6d4db1f..0083f94798fe 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 051/846] 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 000000000000..f1503ed6d12f --- /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 000000000000..a733788dc22c --- /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 000000000000..e89e040a0ff9 --- /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 000000000000..c4c572244168 --- /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 000000000000..30095d20cfd4 --- /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 052/846] 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 f3844c7d3b68..8b971e7064b5 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 3e7b4b390bad..49794e0c2419 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 053/846] 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 49794e0c2419..3e7b4b390bad 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 000000000000..e1842c73f0e3 --- /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 054/846] 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 86d55ccabb60..83c6faac0414 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 055/846] 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 96df3ffe3ce6..93e29edcaa58 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 9c2128469de9..3b03f8c7dfe7 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 056/846] 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 b25ef0493568..0b270e6acd2a 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 9e533eaa32c9..8a5e595749fd 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 0c4daeb731f9..98b696533d81 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 e1a6d4bdd31f..4ae60f7d8088 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 955801e40f9b..ae01c6d3042a 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 a6ea01d5fc5f..88555dc47c20 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 b0a0a276c890..89e9ad26f49f 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 bac3eda5e7f3..87b3fb94dafd 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 86cd24fe7632..3df25691c3c4 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 057/846] 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 0b270e6acd2a..28b05044db6d 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 98b696533d81..c9a2ef499070 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 3676dc5b09d2..401298b2d517 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 bf9dd3c93692..fcaa9b47e64c 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 4ae60f7d8088..d3bd9f66e384 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 0364d83663a0..53e50368231c 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 f05f2893de23..2610923275d3 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 40751bfebe6c..000000000000 --- 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 ec7bc8f12414..000000000000 --- 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 058/846] 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 28b05044db6d..78f98bba2b44 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 c9a2ef499070..bb3bc0b45453 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 401298b2d517..1af4d03c7a22 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 d3bd9f66e384..5cbf3ef028c7 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 fcd1fcd14d48..a4a3cd1d3797 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 b90ce68fa97a..4f0858005f6e 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 059/846] 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 78f98bba2b44..4eeb71fa5c58 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 bb3bc0b45453..eaef1f543d38 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 1af4d03c7a22..2e75de019b64 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 5cbf3ef028c7..4e79ce96bb5d 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 0bd4252c49aa..1073acf6f0cd 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 adf9f4f19219..9d3fc7df15cc 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 060/846] 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 4eeb71fa5c58..3f9486e09721 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 eaef1f543d38..8de94d19d31c 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 0bc6b70855ba..da6793a69d63 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 4e79ce96bb5d..0ea0f55a3818 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 6b207b26b6b7..e52468cdd4b6 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 194a0bfec5b3..4ce9a144ad8b 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 061/846] 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 398a3103a037..d7bf8a147681 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 0c4daeb731f9..b241ac5559cf 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 e1a6d4bdd31f..e1c68b58b863 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 062/846] 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 b25ef0493568..d6298fec65a4 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 0c4daeb731f9..41046c18ed25 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 8f86535ef1e0..42a6c4166194 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 e1a6d4bdd31f..250a7c09f781 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 000000000000..5f1a559f5914 --- /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 000000000000..1159f9e722d7 --- /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 000000000000..128dd4808bf7 --- /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 063/846] 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 3a52b1d3fc20..4c604cd01075 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 62ee051624b4..552222eba2ee 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 4301157e1644..9cfc8d191349 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 a21818701dac..c099c5533339 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 2c101220c5d6..8e0cb94317af 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 de2cf6d7873f..a3df9d5ccbd1 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 064/846] 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 81ddc8c0067c..efb424bcb7bf 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 1ec60a0e6e67..615afee33ef9 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 86317fb8bd5c..8c61b2f86643 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 0bc6b70855ba..39908bff5ed4 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 e1d524c2231e..38c2645d36e4 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 4ca90455bc4d..3bb3eb15d9c2 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 3b8ef18bfab8..7bc8be492e82 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 065/846] 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 42a6c4166194..444f5bb0db6c 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 066/846] 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 444f5bb0db6c..6fdb4cf9cd74 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 067/846] 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 3f9486e09721..77272f4f78b9 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 8de94d19d31c..ff67ccae794d 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 2e75de019b64..e6094edc5d70 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 fcaa9b47e64c..20c60ef33189 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 0ea0f55a3818..e90b9c157476 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 53e50368231c..585944032e70 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 2610923275d3..b62080a073f3 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 068/846] 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 77272f4f78b9..d05819a973a5 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 8a5e595749fd..8fa9b05ca329 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 ff67ccae794d..eba4ab5056b8 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 da6793a69d63..9c9d1a84003e 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 e90b9c157476..feada261a4c7 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 ae01c6d3042a..9040552cefc4 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 88555dc47c20..2fe409b22d31 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 89e9ad26f49f..9bdddc8e1524 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 87b3fb94dafd..acbabfa20d73 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 3df25691c3c4..941d604dd5f9 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 e52468cdd4b6..1b9dde87cd5a 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 4ce9a144ad8b..bef228d4b93a 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 069/846] 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 088e4ab1921f..78e07d25f67c 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 ae2815d345a9..ceaacaaf6bd7 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 4742877706ac..d8d44a74f9ab 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 070/846] 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 efb424bcb7bf..81ddc8c0067c 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 615afee33ef9..1ec60a0e6e67 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 8c61b2f86643..86317fb8bd5c 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 39908bff5ed4..0bc6b70855ba 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 38c2645d36e4..e1d524c2231e 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 3bb3eb15d9c2..4ca90455bc4d 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 7bc8be492e82..3b8ef18bfab8 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 071/846] 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 6fdb4cf9cd74..7d722820800c 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 3b8ef18bfab8..7545235e6467 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 072/846] 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 5f1a559f5914..519200977a79 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 1159f9e722d7..1df917e085c7 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 128dd4808bf7..82790aa9e80b 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 073/846] 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 d05819a973a5..9e85e6da3b9c 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 bda0d5c04589..4dda373738b9 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 33a9478f0588..95921518986b 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 e5e3bf453a08..e411e60782a5 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 dd3fc56e98bc..fdd4bc581f30 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 875ed7db373b..4cae745e7c02 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 57626b23795c..7df3507edfd9 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 074/846] 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 098d47bdd40c..4e1c1f131405 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 9c9d1a84003e..4a9c411d7c8a 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 8f86535ef1e0..bbf14374a1f7 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 a21818701dac..ed48ab548978 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 6d49f50d550e..f50adbc48ab3 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 24a1bdf1883f..f6d87c8532e4 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 075/846] 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 319de217e0d9..000000000000 --- 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 076/846] Run fmt --- src/driver.rs | 228 +++++++++++++++++++++++++------------------------- 1 file changed, 113 insertions(+), 115 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 1ce0300f2390..d3a7e24937f9 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 077/846] 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 f7a609383744..6697ff2f40d4 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 078/846] 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 d05819a973a5..7abefe654241 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 bda0d5c04589..ec198b684b6f 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 4a9c411d7c8a..84e8a010738c 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 000000000000..32e86637569e --- /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 e6094edc5d70..626427c15ecf 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 f50adbc48ab3..6ed9ff22e466 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 e5e3bf453a08..5b4e2906b5f6 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 035bc1e54656..5815550d7a6a 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 d49da7813c6e..623b100a4ce7 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 478254917656..24c6b7f9ef32 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 000000000000..91d9d11e3c11 --- /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 000000000000..50ce2f4051e0 --- /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 9040552cefc4..14562c4d32c1 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 2fe409b22d31..bda87650f6da 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 decbae4e5af9..d80c3c7c1b72 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 0f1d2218d5d3..629842419e54 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 079/846] 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 57b9eafd14db..9e8e0ff30ec6 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 080/846] 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 6697ff2f40d4..c6a3998ec4ec 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 081/846] 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 c6a3998ec4ec..c9180e58fc25 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 082/846] 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 ebdd6c4003d1..718e391e8bf6 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 3416c4b727a5..f613742c741e 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 b5c7a03e2775..fd45224ca067 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 083/846] 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 748c11fac64f..9b672b9ec225 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 084/846] 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 4d4fff883b3e..057d39d4c825 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 4106e5013b94..94380acfcfd4 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 0bf46491d31d..8211a57b5643 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 20d4c933418b..6a2a02987dea 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 085/846] 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 d1863701bfe7..b59dbb3e76c6 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 086/846] 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 a3df9d5ccbd1..91b9c73c9d47 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 000000000000..7028b251ea03 --- /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 000000000000..50d42678734e --- /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 087/846] 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 000000000000..9558dd680918 --- /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 000000000000..3491ccb0d47d --- /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 000000000000..9e65d2f99420 --- /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 000000000000..062e441622a8 --- /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 000000000000..3491ccb0d47d --- /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 088/846] 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 782da249808d..16b46423c8f0 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 000000000000..8346bf057783 --- /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 000000000000..c67166fc4b00 --- /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 000000000000..c8ae6c820df9 --- /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 000000000000..f99c126fabf6 --- /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 000000000000..c67166fc4b00 --- /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 089/846] 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 ed85d0315bd2..c4decfc94011 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 000000000000..05ffde839dc1 --- /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 000000000000..4bc61dd62992 --- /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 000000000000..4f668599be95 --- /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 000000000000..cad32b9233f4 --- /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 000000000000..4bc61dd62992 --- /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 090/846] 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 44b2a5383d21..843beaf32387 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 091/846] 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 9ad1315c1752..75768681db93 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 092/846] 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 843beaf32387..80713ab569fd 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 91b9c73c9d47..232b966f69a1 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 8346bf057783..c64adcf7c013 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 f99c126fabf6..c8233f328bb0 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 05ffde839dc1..3a94b723f3fd 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 cad32b9233f4..a9b06420b333 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 9558dd680918..fd2a34148568 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 062e441622a8..38cb139146e0 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 093/846] 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 80713ab569fd..08a2e0c0918f 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 094/846] 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 75768681db93..b3f5a62d5530 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 232b966f69a1..a5de84293909 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 095/846] 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 6038ed697f91..191ea4315a6b 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 096/846] 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 810a226b50d2..32b3b7f7947f 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 097/846] 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 a599667b8d8a..3b88e4c4cb19 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 781ea7bb1528..3b6041823d87 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 5e485d40663f..e529e441eb73 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 098/846] 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 904c50e14039..a2617a13ecac 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 79db186d7ea7..0cd4c96eb4f9 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 099/846] 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 e5680482e5bf..67a1ac78a677 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 100/846] 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 c9180e58fc25..0f47ac98fd20 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 101/846] 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 08a2e0c0918f..c0b2dac2f60f 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 c67166fc4b00..27841e18aa9e 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 c67166fc4b00..27841e18aa9e 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 4bc61dd62992..1b2d3ec9459f 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 4bc61dd62992..1b2d3ec9459f 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 3491ccb0d47d..581babfeacbf 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 3491ccb0d47d..581babfeacbf 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 102/846] 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 c4decfc94011..b24ec897ef5a 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 000000000000..72731fbc75d0 --- /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 000000000000..1b2d3ec9459f --- /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 103/846] 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 b24ec897ef5a..b6770804e180 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 104/846] 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 95921518986b..0b080d9be2c0 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 b3ad2ad9d998..e00d726282a4 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 7df3507edfd9..0b2947f7d628 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 000000000000..abf0c891b520 --- /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 000000000000..b3cb01fbe324 --- /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 105/846] 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 0b080d9be2c0..1645c5777b26 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 e00d726282a4..779da7e6bf23 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 8211a57b5643..f63301c7db0a 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 abf0c891b520..ab4f960edb7e 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 b3cb01fbe324..5afb5dc45d36 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 106/846] 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 b3f5a62d5530..8092be277cca 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 000000000000..ed33b37c6bd1 --- /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 107/846] 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 83c6faac0414..1eb26d97ed4d 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 ee2cbc3cf540..332c0427ef65 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 6ed5ca6daa0e..901ec8bcc09f 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 97933b8ff851..9a646fd99398 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 c9ca4c476683..561a35625f02 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 12e5483ecdff..240188cbb46c 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 108/846] 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 7599b945a913..522f31b72d01 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 109/846] 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 32b3b7f7947f..c82cf57a4b1f 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 110/846] 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 c82cf57a4b1f..52ca962e7ef9 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 111/846] 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 8ea03fe42616..7bb08797ef39 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 112/846] 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 000000000000..965b606f331b --- /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 000000000000..76219eb72e85 --- /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 113/846] 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 2cdf96714195..4eac571f9662 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 4ebe2e2852fb..73758b7eeb7e 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 30f39e9b0639..541225e63510 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 114/846] 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 1c0be7278346..043a79f20019 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 64abc9fdc717..41f125d48398 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 a5de84293909..2758b9a7e760 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 000000000000..e6626d57a772 --- /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 5343dff9da1d..3e92bca986ab 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 d8c9786541f0..bf753a732f00 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 115/846] 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 043a79f20019..11586083d8c7 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 116/846] 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 11586083d8c7..7514608bc7e8 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 117/846] 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 7514608bc7e8..76baf27fb2db 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 118/846] 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 1645c5777b26..7fa97b246991 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 0b2947f7d628..84ec53702788 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 ab4f960edb7e..3787ea991445 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 5afb5dc45d36..b765727c168f 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 119/846] 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 057d39d4c825..902f3d56c1e4 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 d3a7e24937f9..70c47b426825 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 120/846] 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 b12c3c344ef4..2a81170e49e7 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 121/846] 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 2b51b7320758..5328773a7389 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 122/846] 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 8edf0c23860a..5fa8009a8b42 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 6675a1029bbc..a8a673343bf6 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 123/846] 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 3b88e4c4cb19..e556e5d59c18 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 5328773a7389..5f14fe97afef 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 124/846] 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 6675a1029bbc..eb8da9dcc88a 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 125/846] 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 1d3fe405521c..912e6788afdd 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 403eff566ed6..771a26fc9a86 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 126/846] 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 2ec0b5a8d6fb..f5bfede75a76 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 624e5ef8fcf1..a29b832eb601 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 7fba971cfd88..8fd0093f4fdb 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 000000000000..7da26f8ff4d4 --- /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 000000000000..be7b4244bc06 --- /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 000000000000..6e5fa41fb08a --- /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 127/846] 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 6fdd282c6849..5baa31d5cde0 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 d99235f7c07a..281037ae37c9 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 000000000000..8617445c8a60 --- /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 128/846] 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 000000000000..4feab7910b74 --- /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 000000000000..923db0664a71 --- /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 129/846] 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 2ac9057199ff..f7dae3dcfff0 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 902f3d56c1e4..4f0ecab393d0 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 779da7e6bf23..3b7e9739211b 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 000000000000..86cbfa8203d5 --- /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 f63301c7db0a..1e94ca00c145 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 000000000000..0263e2f5f20c --- /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 000000000000..feb846298c65 --- /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 130/846] 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 218b0d27f748..9c508fc0e4a1 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 dfa6223f1b9d..5f794598052f 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 1c4914a470c9..7bd5f09f333d 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 131/846] 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 6ed9ff22e466..3ac99e246841 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 132/846] 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 902f3d56c1e4..6475fa67d251 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 f63301c7db0a..b9c84654593f 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 133/846] 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 6999b6bd7404..836897927b01 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 76baf27fb2db..98391732d189 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 134/846] 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 3ac99e246841..8fcca4b7bb9c 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 135/846] 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 a739cf7ad814..67c6bdf8873b 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 d90c49f79de6..c6e465b2e4c4 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 21ccc684ea9d..ce9ab2f1271a 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 136/846] 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 8fcca4b7bb9c..3fbea77757da 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 137/846] 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 3fbea77757da..c95bd5d72bcd 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 138/846] 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 c95bd5d72bcd..51d7d9b3ab71 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 139/846] 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 51d7d9b3ab71..6866635b904b 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 140/846] 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 67c6bdf8873b..000000000000 --- 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 c6e465b2e4c4..7d1b99fedc96 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 ce9ab2f1271a..145b3c62b062 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 141/846] 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 6866635b904b..5ca30d598eb1 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 142/846] 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 7d1b99fedc96..2992abae775b 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 145b3c62b062..56f6a855dfa5 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 000000000000..18a31eb3deee --- /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 000000000000..bb58483584b3 --- /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 143/846] 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 7bd5f09f333d..194354b291fd 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 144/846] 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 f7dae3dcfff0..c00f84bdb857 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 38cfa212d9f4..f51855badff8 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 000000000000..65830afd0f8d --- /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 69578732898f..1b82f34c8634 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 000000000000..2338dc6e5940 --- /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 145/846] 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 65830afd0f8d..7d7097a81253 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 1b82f34c8634..b5d9ef0110e2 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 000000000000..e69de29bb2d1 diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index 2338dc6e5940..c0350f243c7b 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 000000000000..e69de29bb2d1 From 8590ab4d46de4eb43e7ebd42cb2f13b0064573e6 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Mon, 18 May 2020 21:48:35 -0700 Subject: [PATCH 146/846] 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 f51855badff8..e7a4c1ecaa95 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 7d7097a81253..d70391999a02 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 e69de29bb2d1..4b18a073e1ad 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 c0350f243c7b..f4fb70b7b1dc 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 e69de29bb2d1..36a28c04b1c5 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 147/846] 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 d70391999a02..31629a1dbc1b 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 4b18a073e1ad..d536dc385d53 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 f4fb70b7b1dc..9c42d401755a 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 36a28c04b1c5..3d26ddae78ad 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 148/846] 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 31629a1dbc1b..ea850955db12 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 d536dc385d53..722675a6b71a 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 9c42d401755a..601621ffa9f8 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 3d26ddae78ad..b757c8a6176d 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 149/846] 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 e7a4c1ecaa95..9e826316f218 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 ea850955db12..109845a28f44 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 722675a6b71a..f6535c8d8f5d 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 601621ffa9f8..953c573d406d 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 000000000000..fa6a9a0fb10e --- /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 b757c8a6176d..000000000000 --- 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 150/846] 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 109845a28f44..f720d14473af 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 f6535c8d8f5d..bb88df1a56c9 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 fa6a9a0fb10e..291fd5500f79 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 151/846] 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 9e826316f218..46df743b5bfd 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 f720d14473af..c0858ec4c888 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 152/846] 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 46df743b5bfd..fd832d115777 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 bb88df1a56c9..4521ae38d495 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 953c573d406d..fdb5a8233695 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 291fd5500f79..b6365c1709db 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 153/846] 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 2ac9057199ff..714e25a32ea6 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 057d39d4c825..7c16dbd8f267 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 38a5829b3f74..dbe41823a9cf 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 32b3b7f7947f..7cb04d4d81c4 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 28183810df48..a971d041ca66 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 8211a57b5643..79da1f3702e3 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 c30d23de3f86..7f92d0dbdc97 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 94bc1689619a..416056d3fdb9 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 80e2d104f824..1cd6400b0195 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 000000000000..79c1db87ac3c --- /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 000000000000..ef9a55f3d997 --- /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 000000000000..bbf61df0cda6 --- /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 b4227eaf2f8b..be37dc16b9a3 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 8884c8e16129..9113aad90dd7 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 154/846] 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 0f47ac98fd20..9f7bdcb1be7e 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 155/846] 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 c0858ec4c888..33d8331c2923 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 4521ae38d495..779fd57707ad 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 fdb5a8233695..0485a5630afe 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 b6365c1709db..903b6e5099ce 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 156/846] 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 c00f84bdb857..086a1141be55 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 fd832d115777..03addf1f4a40 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 b5d9ef0110e2..ab9542a7b9c8 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 157/846] 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 f7dae3dcfff0..fcc9895dd901 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 158/846] 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 7ad272ade5f9..ccf8f61c4a92 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 1bf4538b7c94..178df08e249e 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 ff6e3346dfc7..99c512354d58 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 159/846] 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 98391732d189..e959c1a65112 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 623ae51f9f08..3d5063d1a3a7 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 160/846] 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 05e2650d0b71..13e61fe98bac 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 7f2ff8b9b26f..05517f6f9f0c 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 f49668082791..4ef963ac3148 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 b35a8facf8b9..afefa2506381 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 a5871cf0cd4d..cb0fd59a2d40 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 4e1c1f131405..d7819d737ea0 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 1ec60a0e6e67..7227683aa5ac 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 e3e1136b6769..5f0cd1ec72ce 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 5206266ccf2a..37e24ff34f7a 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 161/846] 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 17639cc2a064..575462f25e61 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 86317fb8bd5c..3a912d928375 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 5b092526ce4f..1530538aa7d1 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 c24a24733d7f..9b5f1dee7f44 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 155a93de4fac..fdaf37e5e08f 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 d5dbd495680b..e91fb0c2f27c 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 fe34d33fe652..d537ef3f3238 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 e7062b7c16bb..c8576bcfcb44 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 162/846] 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 ec7c4531ed71..7ba43562d7d4 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 94380acfcfd4..146212cb2c7a 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 e1d524c2231e..a3b7134a376d 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 552222eba2ee..ad39e59d0678 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 67a1ac78a677..58a8e1a1064a 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 4e1a8be4892e..78b15afc5a7f 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 efa77db822dd..15b129fa0980 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 9ee875d7516e..5880d1d61020 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 9c508fc0e4a1..fbdf927b8247 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 4b2586877e56..d866bab2f642 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 4eac571f9662..c77b44e0c99c 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 ea654467b866..e4361b00fb4c 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 d5797468e9d5..fe457aad50e3 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 30084e3e1ffc..a2c35c426734 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 11360b0ef849..68c36f918918 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 8d767a7fec88..2e853e8301d6 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 fb3706be1c21..a7c4f2c2291f 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 2c51271e312d..f84566ef707a 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 163/846] 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 52ca962e7ef9..fbc29efdeb26 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 a3b7134a376d..51282ab93ef6 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 1174f4215774..a8d4c7620b1e 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 4d8d4438d881..7247518e19b9 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 32d9a45c37d7..b637253bd026 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 5f794598052f..22ce484b24e4 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 fb4700d8743f..0820385e01bb 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 ab9542a7b9c8..6b6e2c7324c2 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 164/846] 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 4ef963ac3148..5a7f759486ed 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 5f0cd1ec72ce..d093025fd3d7 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 37e24ff34f7a..74144fb299de 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 575462f25e61..92812816461c 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 9b5f1dee7f44..325b6cf32a3d 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 fbc29efdeb26..a8d5c10d5daa 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 165/846] 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 ed33b37c6bd1..dbc434505947 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 166/846] 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 fcc9895dd901..dd0905e9f399 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 0b80cce6d23e..edba3b4741cb 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 167/846] 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 6c1488664bf0..7e07e7751e34 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 168/846] 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 dd0905e9f399..a8ec5fa67b62 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 edba3b4741cb..1ec07b9dbbe9 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 169/846] 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 d9776dd50a83..e845ef99c7cc 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 7febd6f37613..12290db3dcf5 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 a643354e2438..895a301e8212 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 f678f009621f..648ba3ccd01d 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 e69de29bb2d1..000000000000 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 170/846] 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 146212cb2c7a..6d7af45a4722 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 171/846] 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 9d69fa8a7f69..f0a7fe52149c 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 172/846] 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 0b80cce6d23e..6657ab4187b8 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 f0a7fe52149c..391952ea6b14 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 173/846] 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 16b46423c8f0..c40a387d2979 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 b6770804e180..6c42014b4c8a 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 7e07e7751e34..2cdb6486fcb4 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 d8d48eb15358..511518082bec 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 174/846] 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 c0b2dac2f60f..1e032a7bc20c 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 194354b291fd..11b3f69a828c 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 c64adcf7c013..ae0a60329962 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 c8233f328bb0..737e84e963c9 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 72731fbc75d0..278bebbbd9e8 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 3a94b723f3fd..4f97b0113340 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 a9b06420b333..b4b49bb369ac 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 fd2a34148568..3e1a02cbb3c5 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 38cb139146e0..f844cab09ba7 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 175/846] 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 66722786eab4..b6d50bdfa146 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 dbe41823a9cf..57c62d739640 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 a9c0ff24fa60..78d69690c2d7 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 e556e5d59c18..dd236535c18a 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 5ca30d598eb1..bc5fe44b30f8 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 7fa97b246991..141035a980ad 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 2cdb6486fcb4..9a6750c51abe 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 176/846] 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 7bb08797ef39..2045ffdb5f09 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 96d55771e6ce..bc5978b538f1 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 177/846] 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 3958ba012467..0c80394f03e3 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 178/846] 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 000000000000..8b877f696afe --- /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 6f8923b26609..6bc9e23bac5d 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 35464f629c36..3c9397441735 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 d5d07ccb2eb0..bb191f9be92c 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 179/846] 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 128a22c86e36..eacf948b3927 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 180/846] 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 8b877f696afe..6d3fb317bcfc 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 06638e7187ba..39410acea4e7 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 23645d48fe79..09614b8c1ad7 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 181/846] 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 1eb26d97ed4d..45de4d29375c 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 332c0427ef65..79e482eec303 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 901ec8bcc09f..b2e8bf33771a 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 9a646fd99398..de83c4f3d633 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 561a35625f02..264d3d1e95af 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 240188cbb46c..f23d4eb0f9ca 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 182/846] 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 a4a184480fba..adc945a69441 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 6f8923b26609..9809f953d670 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 000000000000..2723af03c0b9 --- /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 000000000000..69a7b6c051e3 --- /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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�jR+WHI{J)^Xl!g`1UYXNDh&^IT!WmTL*I+@b5lW!)l)#HdxLJjOffMrGqwbu4hE8g zlqST46;amZK~7*xN=h_Iv$RY#F)=bWPci^C`intB{i&o`X9fvXPI4|JCSY=<1E zZ3#L%0A(XR_>4Kw?qfaF%OpVFH#RdcG)=WMH!?{|u|Q6OpkaK>6IU!Olgx}#(vr-K zk}M3rK;|M&TmiWVcJ~xKxg!cI9EPSEC8k;?o2MF?npq}-&NatvXi#bzrAZs% z92{vIa;Fd^n?XBkpmXjFjSNf-P0TDzl1;$JL|4Wmlu+s>cpAdxBm;z9pm`Nj3k!2I z0}~^2b7PYv1IYcekUUpVQiK_8sfMN|X+{<)X~v+%T%d)WcthB?6n4xm+2sc!ka1-~ z#Gwh6$;sxHpcCd&EfY<_%L1T>CV;CWqOu_P^a)~>!>-{bNfQwsf*g?u%2lA%`>B>` zCP_(&Mg}I9@O~-jHbU=>0oR^-dZ5G0_4FKzlHpYX=9byyocz30Jw0a-1uAGw!2+Q9 zd2`bgQws|V1JIqthK8WCq(N#RB@d(`u(ASQUJ3TADbj9Th^(GoNCv2gCB@DpQzJw3 z)HFj&qqHPg6^CBI8yZ5)2UkOeCds8m;9eL+kYHt>nq--jXkd|Qnq+EdU(j8VhckZ6G<0!XBxxEhpyp^b9L{kH^eV=^{3NlCU$HAyxwNi$DMHFaeG zwNpXYWTGcGP-_<|MsA|8L-Z!dxwX^W(84^~B+Ven)X>x@H3f2-GswfpJra}x3v?A0 z!9vQ=%sA1)z$Do+)zrYu#MB6UVid?&Lg5Iz&dI{SJk>PS%skD^*wP{uRCq&Auq0#v z^0io|sYc0WhGr(IscA;0=1E3$iU>pGh@dPSK?CKMpbKCO)6z`L)6Bu)NJ6=1VQ6k( zX>O93l9+0eng+Ts1DcC8G$CDnNTp+C1-{qW%E~c0IkmVLt%$|minTN~PfE2+wlFa; zF)>LqG{tBd=j0cc7NzP!t`GsmJ$wwo&-F-b8u0xjVH-RYbR4k(aTl<-Hf*D|;yu_QCuJGIg{ zI~8n(Nm8nbsd-wONs_sF3aH3|ra7X_fZTfx>RuV7nwwi1rkWXBg6>x&!3;~#z1s1h zHSC}^Z?dT+=w5=fl$4Y-Q&6J{Y7&lM1np%2O|uwSCM8-LB$=688knSjb})pcCfjIe zfsQLHwa`(3-oTt%0Xo}7LCIMObQv>LRui&d1~P(;c~cLx8*iSLXpmx(Xpv}cVqjqm zx&;k%>lH~h5FIhlYxhh{4J;CklhZ8CED}vjAV-^k%)k{C5N(#mph0YNqf~P<6N^O9 z^e%ju5z}+PLstgSTmfm`g9IS{3$L(W!)0ljl$2;}mS|~i znv$9Xx;7olHC)67HEh)axG`;IMV%YDQqxilQw&T@l1-CM4M9h$pxwwt7snx8l$8Xo zWQ~#yERqw=Q$ef8LBozkWvN6*8KGdpOe;v%n;0dV87CQ7Bqb##nxt5m!$X8<>k$nj zNU}k{vdbXR(9j5U{*IYts3hc@*VgnjsKE9?h((PU5=7uSt14vR#3@p-24I#PB z6r6}Kg97dLE~BJm0~1pN6I0_fOA8ZI(EVcI%@&}`y~qd=P|X9s$Sc*tAk8Al)WRew zB{?z4z}%Gqe5faB#zOo7AuW>fE8^3NQbFTlDF!LV7G{=)Mkz*SW{IFS8%z>5NL`ki zY-L4KS&9gGkUxmNqR}irEe%?L8JeUTrzTsNrzV;hnwh4$GC-tA^D?-(K#~ifTeu7j zj4ezIEt1nr(o8K)Q<7a75>rx0v)cl41}`}A=;@WEChO^irQ++&Lypaa9kB~N7zK34 zPDwuUH5HcNwFMx>py7u^OM_&CG)pt16a!EKfvzk7$&%<=$PfUsYd!NI7ULO!0NF$A z1u>~<$!Td8iH0ebNuYbbETN|Ylb)A}SwU@PkeHg9W?^ccWNBe+Zjl0-jx4AI4Zr4> zr55EtMx#j!A$VGXt#~#wOEFF}HcT@~GcqkCpmk@K#)(Ge28l^#=0+CAU|+$d8Olr2 zEEvK|(kz@x!4oPN27(lrLlqbrL52dwG&2Ql&oH!1M(UUjz>(0YCI)6kprh+7($Z3)m;Qna z6~nbY)w9Zb43J3FP?Wlw?bb6q8g#^R(2YGy}-h&B-tYp!@^M;Ap0x z9HVVvVw!AZZfQ=ZjviwZ^ArQKB+E2&6T@Un z@L{p#i3KP|k<_dKO@o5&w+1yC%ni)UO;QZZQ$gcYpk8ujaXd&4#dLxW&B;#$9psc| zZk}qMYMyLlU}~CX0Xn!DBmprhJ|#6Ru`~y?$SNrwTwQ^#+JZ$9yaNkqkwR~~fX^O- z1}63Nz`97iINZDlekrz*iBXcFQL1^W8EBIL=-LSANCI*_1#y6(8B|+*T4GKP=uTD8 z%{IoV=EjK@X_n>&2B2{ka2*f2zZ|ZJ_TDix0*69;RccW_I17MQ4;h&xT9_E5S(qmo zK`xR7rFwXfqOMedcOw#06D?AW4NQ|PK)2?lK?+ikM&!`Ip*PLY+#=aB#U$A@$->AK zlx(0OiNjJuBS^_wng?1lV`!dXVQ!piX`E=7Vs2uYm;ySt9a#Y^7;(DB$~N0!24uwdH75Pg`){5@1SI0LrC!z56YXM&DloAW|o$wreP(vpqLQp{6~lR>32Y)uG(nk+Rr*)$Dw_PU{Il6h)csw+coBHqoi zkgaWqHiMNFD6@mzVr3OnT3q4;yX#s<0UQ?S*D9Dp`byxYM;gZA_gD`%M!OT*CqxB$ zKPsppVw744Ivv`;z%b1$#Ujz%%+S!>7;+aCI4X^ka|<9BOqf~5gX$f$LK4$(&^;1P zAfv%;AmrO6z;{kSEKD{xGP6icvNTFGG&3{;ou`&s2$>XKQUn3R*6!hjMp45UXQY$*WgIuUsR zS5idjE14UonHm_HCK)7~Cz~5s8o-tW#OD`5w^fpo_7Ik%MLi6xo&c?ehH z3L?Z({xrkXL~{$XB(r1-W6+I=u<)_LW3(~k{_K>>JaCEz6>xfbiFuWJdX9OOpoDIe z30lQ%ZfuZbY;KmCl44KO|?ie zabbR3TAgY;2ZjnrxDsYG!I_Y6LpdAhQ@YzKFUs73>3o5sn#jY39aemL}#& z7KW)N=Aa9RQQ`_(J!7$oYLRDVXkw9OXlZC@VQP|S4k}RNqx=jFAU9}&?FZjqpP82q zy0WzhYefNGzz4n=8#M9Ed3Dl7(wE)L5rYQ!H28L69eh$1ZWCq^XYmt&@X<}rU zifHa)Z(o=e6hW@YHZw|2PD)NSNlZ;LFoj*J2MP)7`pn>CROZG;X(mRNiHV6x<^~p^ zv1eFQ#5gxK*96q;1NWnhElf>Pl9N*`O;Xd6j3MK5&?OC!oAj)#po_gVG@&cIF|HH@ z2Ls4aS{hiS z7$qB8CYmAj+(-&Xu%?O4tSQVm+>V1BZd#OB4%+HyZeo<2m~5D6VVIhh3>sYr&y*DZ}t6c5WYhABzrW{HW02F6Bd#ujOb&^$w| zG59i0BIwfdBr_u;lf+chL?h7Q_&72SQMMuF9;4J`a|?qsvlL^~)U;$!^$*TH#2SN? zh0;>eEG$wCOh7F(V?)rm_Q_NE=%neM;lM;s91(kPzUE=S}jbIlFTg4O_M-_`pKYWNwA6$y$Jxi zl&mDa08*`j&%a7EvrIEdHcqlIOEopLbY&<`h0BA+lJGd(&=9mjJiZh*z-MM)oRpMg zoNSzCVQQLWWB^IsP>QE5L;f zsuB1yZfat3Qj(cjqJgQInOUMqqALR^d*P|^aNC5GX+is>EX|WG(=1I=P0V2RC}GFu zrsg7L*wjSO5Rsu-vYDZ&p#f+QCM?6^2{@D#V**~$XAD}GR1#mDnO=}t0ICNq6O+x& z4bm(O%?%CB3=P3k=b(fKRRK#Z@Eij%#yrE^%m{Q7N}^%1d76@(iC*1v$=&~GUzf|3u8p@ z18yaXHY3nlM9AJn5Lr|bpO}-Go(CScw=_#iOE$BxOf$1IGBYzXhn)6VT#}fajj9&p zGDveNH#OG`KJIT}nwV;4oN8`lnV6PhlAPko0FkoE$xKSNvMSBXtgy1m$;>OQh|kH) zPKC0qtkUyJt*nyFOsuRbEX?9TbOB^MAwIFBq$s`woLEt!4aG}_rl8g_k~2Xq=p>`G z6tff~!(@X*Bk&S+G&MN9U~XY-oNAC}k(OwjmTZ!2K)x4>Gt(2nSMOrF-q55N9HyY9 z9%(uG<;I!01+eHeNij=HG&8VBGfpy1ONLGwUn#TC#M4Knrc)vM|FCT@>SzBFJK6 zi&Tr`M5AOw^VB3mPz_m9RB4lGY=otE1*yOcjgk{fk~88<^U5>9_s5u+m?WnfBpaJ1 znHw0TflgFGR$v34#?{F*Hi9P*v+loUuy797dO#U({XkP%$?tP*r;%Dkw!ASbgV4;=Jn=4L4-N#=%0 zMkz@~Nv;e~IUCS5*LDoSAccOgV`!j)u;v=r3S(3yATI}i#w{&Dmv@6F13|TlL5eZx zum^Kf(^L~fP!@s9BFu!YtAqq(aY>P$UJ%4=xS5#2NXj}zQ#0d4^Asa9OM{fuDODGT9~;qNVWLU0Nt!X}a1&S=fUmlTF0C_3vH)!Z zFt@NUFgGv;jTC|wB*1o^p(hMTBFC4W;EfrCPw}||G0|#ZkeX(cm||&clm^<>kc^mU zEiNg79%@otQeDhZC2CnYDD8mFZiCM6n~n;2Lk_TIsBr70-a zftOHQrlzGOnVOrKn_E~Wf$kWFmeT0Xuqe(?&dy5&pIKsTYMEwcY>}3dW@cz)o(7pG z!0&cLLy-Q|63|&IAbZWs63vs1QVqJe9ra@Cu%#tn54U-I@xdfk+LsJSM%{4p@#+nY`8i`KBMoGpghNdZ&=0=tV zhTz+Qp$QX98a7Kz0`*7}Q_PZ643iSUYs0ZQ133++nx-aNBqygB7=Z?DKv#`H&BsW9 zaJQo*K+v>MVq&tnrKO3PQHoKDc`9yuEwLLxFb$h0rg8U~Xt&3|hGZsx&jrOmvV3w?F|@T#%X!A1J`G!x|RC zWct9w+|WG9!ZO9u+``hx3@Nq3gUBeY*wE0wATKoRrie z3(%-3)P9p>(Cl!Uk-4#jfk`SzGfWbmMnLNq5aWp{=Emk0i5BKY7O6%?iJ;?v35+L# zDi%;}8()gF;ESj`-z<{z^GXsk^FW?7N=`B{Pfjy7PD(XSG5}SCFiD$I3p<9YOz<$2 zp&_KF1!+7&I&g_8i3KI8Ma6o0Rhb2PdQq7LppFTs;fPr5h|+z=rOh+9AO}^edx#2n z_YY{u*d)a$*(@n7CD}B|#2j>R0H_xV9cTdGp=f0V+K>l2ek0Su4%UzYoumwr0C@vk zqZk?(f!AwN!4I%)fgm4&R?+L}ft&zhfm*FFr>CZwSR^Ky7^S6I7+R))$|CgiW1L!1 z0&43R8bGQS&}e`;C{9z0L2;94ZfI&?1iHr~#XL0`+VCCV4N0JLg_2E;%ng!FQVh~8 zOdxx(pz#3;5777qBw>K=zbn8Y13o1w1uBUYjo{eQQAo?vD78Qu%1JCv&djv3axG3y zEJ$^Mte1ktHC8!LCL7RgQf7(CsivvPrb$W3sVPRFnF^$Iiylt+Lj#mxt*o$l6ExsO zIGjLXVF~GjfsE4A16i-9hseJW>p@x26O?|zeK9OKm&jCakZNvV0=gB!BE{S&#SkTd zpvANfsECP=@}uKcCnG~6%S1DyMDygdl+-lP)I3tWU=3qXm|0nY!VEcSL&6MK0wyZ` zg5m{S8>8-2bIbEBEwKRaf`Z%*YiybXx{@4p)MT z^D-+SeOgf5<1h!I$j|^h8V~C`CRwCeCR-Y%S|%GN8kvI*w?#;kY#zbJqfwHfaZ*Z> zL7I`dsijFO(n3@00|#ko21dyS28qUI7UsrArpZLjeZWo*gw25x*O~{tesmNF^}*wGzA%fa=vM*fst`q znq?C7h*un%5y{4~AT_lJmS`-D&61N-(+ms~Elmv5KsSNmZiHYnf?zr^Pf9jPGzP7o zOg2t2Fn|n-(60AxoM>!eXk=uWVwh-VYzSJbNmR5TyoRR>Pfb^&jkzbM7+ad98Kjw} z85<_2fHrwSmcyljkDt~+?p7hM89-{|>nM~O8$l1t!kVbSg(LR7nP_T|Xq1$cW^Q1V zVqpy0cuB;d9tn*;1?p4*?e&v{v#` zOQ0@=>jJH+O*8NU~O(!kKtGSx6G$;{Hk5E4+d zD^rbAlG98LjM7Y!EmAGb6V0&>j>8H%)Di?1N|1sEqcUJf%FIgvm3y>vyoqIEnx$E) zrAe}JnvoG`z=fI>0wNjVtPnss8~KEI$do#$6a!WBpuQ6&^Y*5url2hz<|c+F24>*7 zBjh#&?qV6?a9mM?QRbt@ETqcx{aqX6BJkTAy@YM`%T zk{727P(lk~2tiL8np-597+aVpS(v1x85%&QU#MC7q4hD0EK)2|Qp_w;3=@qK4MCg# zh$|nE(+niFqNiaB78)`$|(!kQlEXmBm!Z0m4%>uOS2C3WzXDXc82Nt(@+zoDL zWA7Ikni`}g86>5enWPz;S%4-QQTs*2*-hinNi#CAurx8TOfgDLu`~poeuxx01j;XX z@DS+ez|`TZ*|3)ZnAy|JFxAk&0CY;2VIt_bT%xmQQEGBoJjibZb5>envbjZ4l3}8m zfl-R#wX~`y*$*HMH$wo+1`|y@A+#q9^i!$@_Qj5$W^9%4HWN-%#slx|VtfvQM zK>eYo2lWW3A7heQ0bW6wlAH>fjU{wM9gGH@GAvGe01+AQiG{ z9qd``R$3;S8d(~rSQuIunwXk{#!OKokZ3ENLAO&Pti+xGQFDQzfonx(3FtWY(qgmX z%=ElO(Drf@`+WK$}cugPc11Z0Ov*XWMfOyBok8; zOYw5YUEt(6Z6gqB8I~FrX!YNtR{?X@R&JR&sV=F-h;<&2^Y}ncH_FThufR!(hnQ$+o{^Ml zZfufblwxL_XbHNX48=zL7Jw^KsIewNsfj7*W*b|WnwuM?8m5?77+EAHLrMl{(SvO) zB`rly59WHJhLIbgO0dMiBBvsGz2X# zEC5A`nF;8GQKMALv}8jA(6M!u~fw6I#8R$p>P)iB2^#r5rM#;5U;~t#VXxJCANKQ3MOSVilNHH-tG)_#09)%4V z5FyvS;M9XMS#AcIl}4FvHZ)5$GcmPHN;ODJGd2aSnn6rA!_&HsMy9b5(ovxJ@|Okd zE=|zd$D~wKW8+j4UmOwes!W+vdMh91xh-quFpb!I6^Mn<4RB8)9el8n;Q%v>437oA~7YBBUy zGf+wHl34_C2c(^d-aJez1`U!WrnqDlA(zvj#+IHQ*m!Igt67+uq#Bx97+RPmnVFcH zr?@f%gVucoBo>uqCg!*ng0ea|Kq0H*3{6m_E%WkA;!_Jt!IgBPiGguyvT2%88tCjD z(?nMWxU5ZdsYNVosS1*L7LYY@@HQ{hwYdd)dT=Lz#{5)DO_NldR7y=#z#A#R9)z!) z0LeMR<&3}^9xPIm5|b^HQw%MVOp;ST+gLs~+Xv=Qe7N0n0JBo(mr z@o*c#Tol(JTncebQnIl*=&l39WP>!r#6(D?3l0Xv77yrtI>PP zv{@G1s|CA(MxkH|I=#~<1-zHn$Slzaw2lfX6bvDu0IJDM3KB~)Oac;1GEC#M@-snO z1T8F*O-&6_%#DoAEez8@CzgU`Kp6s6hY3`JrD2k#MUqLPX>tl^|5~am14sheTmx_V z$}a|OgtM~pFAhdJdJ?G?MO}vr*{7ZezQ!pYx+WHsoIzLTm?m4M86|@*;{t6@Mp6TF zA8u1Y?c;dxJx!VUc`$R0j8j1OHJO_mo0}V2nxujEqN1w8X0i#W{brV5keU~tOt897 zGd49aOG-^KPD)KmwFDLX=;MX(y(P(piAl+6Nl8X#hL%QViC7LPFiI;%w%pJpH4k(} zRXntbVrZU`VrF7)o@|WfEpTt0urG+H5qg+ZmMYtXns67 z)c_hGkOmScD5zQUqmGprn46@SBw2vArzV>jg4Wlew^P8W3ESR6^!6z@o0~xjS!@*s zDEh#~7j)7XQvPBdD-S+)43tMe%ZbeslMKvKlMGWq;{u=~7C|8gk^}{d31rqY1ld$r zl?lEQTu(0~AF>e?G+<#_k`KxgphNu3GYmkd#iSS-7@3-;nk9nH(}Bx^ng(FcfXfSf zO%23Fk)U%6Elg9=%#4i^lgvQr4RK)_$fH(P;IrHaY!ovt&M3;v%Let%(lSAN84^v+ zl2R;Ejg2i6EewrPAd|h|bd{EtX$HyTh6a$4Ui8vDKd&scs6IT0;}mlv^CWWv(3T;PgpC?jqrpZ|EB`^O z025O)BeO*FL}Qbb6rF}E}|0M)bLc_ru_1<1?RTs(uo2?nxZ)dX~LgrO1gjmgmbYno&XIyooBz%b1+ z*#y)%&r2;QqVoXSK2wldggL8elvWJhcmt9oSdAMQCWA)dlFd?5%uJ0z7suhQ#tjV2 zO;Zw+Qq7ai4b4rGz%4%7#k#q9Vp3A7QIcV*fr*hJXtNf2#fiw|PNivhBAa$@v`k4d zvM@1COG``y&4!qvOoV_7BII6fF=Xh_v7`jwMo&;#h194iwgflGKz;|^A_O{i*}xEV zMM0W@SqkXlF;H_2Bunk`0xb_3TN;}gTAG_$7^WJfrCOr+6m)zF*tb~o8oGy!%)ve? z$&UvO6~K~lW^r+5K6nR~scDj>nQ5|RvW1ytYErVHD+3<&AYVh5>w}v_ppG-r?l*9? z3vCX8<&mafz-#W%)Vo%cfGQx^ra1#kO9Rjal_{2>)80V4`>8yyhE^yWrCKH>S{Ni6 z8=4uVCWDSNL!PmQ%x!`SI8aLuIz$TM`lgm7rX-dm5}K`qHM0#(F+&}6C#SJ-N}8pa zd7^1z5{^bX=-5s}QxF-SmYI_p4?gf2RF8sAOG+^{GD-qXErBlQL{kHbCs0B#%FG8} zyk!EC2c1Y_X_AtfVrF7ykZfUUZfpQK$`VrifxHIxDL8Gxc;G^nc3C0a2z01} zxmltG=*&S-=E5uxS{ZFqnU|inpvv3X_|?dS*j`Y zmRJ;}nD(1PoB^6_Fa~ePGXqsu7N#Z^pgRYWlT!;yOkhb7ZX>2K1glLG&>RwI;d*jv zvau0#!X3qJaBn1<8zowpS(qE8TBMm-f==nDav6eJP$wBCrkH}yFtxNyO9l-VAtwpQ z3>3YSJX z2oSVax-2mVoS8x4hG}j(TqCG_Nl6AROGyMx9+?`YnSeX+P-}6T3vT3@fNZjG0dHo{ z&x5wPj6g?#CZ(DrfzER>g02q$D>u$7%|$_@SQ?n8Sfr&T8-R8wL8NS8=1{ZLM60<#M@U$jrdXPqg03zD9eIab zb0N2jLGe+Rm@^>drIERzp`oRDs-bz3QL=$y3Q8*m)Fs7O$p8@qx!KAJmWb>I#O)>) z#^#Ah$rcvLpgZG1v*t({8{$mJ!EIJnkgflaN))3>hR^XxV@sgMHDoNnsWc6_83nQm zspsxil%ES8)DLn~0iVbX$|@!xr-A0aP0S6wv1Fs8=@{0{E(n0ApzAUu})Fh2hE-nF`RAZ5BWN86fF_Q$^{a|hc z?*4%F!p|`?0UZqnJ&Y)+vLv+_e55~g;f=X@nq`uqc?#%i(ZpnB+bw=#W>L@#mvmel>saR(uKE-fbN`0GdDL+voK3BNHR4sNdz6@44M&# z999I54x~{?==M;^1SP0T4O+1c8vicRfgUG_b~qDg*D7|Sz+nzbHda=|8wal$j8TKv z3^IxcIym3R+}zU4G|AL5#oQzXbW#Jn?9DGIwt(%_#X2RLVw{+2U}$J*oS2%DW|-*8 zfS3|Z%hRBuH*|0r2KFB~(o8^Gsm&mp$t^%^(76c~umcoK(n^a#=XaQdG>%qO zoa&iZ0vTLIoRNu=Yb?P>3C5%39nh7!#%2~qCaEbVpnHOoKu4!wQ3pAq5#mAyNCZ&e zKn70++6DlFTWN7BG=K*n=n<*L0(NGBp)trwpmGs>Vga<7V`7wI4m#1*Jk2uI&;&GE z02*3?MC<^#8(dfxR~DO;=4DoxfDYTXOa$G)0AiVf2qVy8oKmlN7<(QYB2U>jxbuv=F4pkms3m&?Q4LrK7rw2Z( zT~7~sod$STJ_L0h96E9W3O#(w4h#)V43d&9Q!NtBEx@<2fmbMijxGU3n;B&G1U&o# zo*V(!GVl{bv9v96^7B%$t#2?eO-eI0F)}g)-3|-BKn*m*0QL=JMT1F3VsbX};iJh0 zX6A-w$;O~ty;H!a`GA|HWvR(XjS#SV;A>IAQ4blkg&1mtcC8^=Kp|UaVQFY#X=Gqz znVOntVGg=KJU+_L400hOxLC#XEcgTvJw5PB49`61m0D<4fSOd6;A^+yAtzuMSz4N# zSz4GRCmAGyj==y8-lwEifMjh@8#myx0^$#IbMPKZw5cHQ>8N0{p;vnOmX^TF5wLS% zvsP%1LUE9#d0J|UK~i#JnxUbEnGtw-6NZBz>+8Vb3mvipN1dKtS!%ML9_Z*;pAcvp zved{x1tsy}ccy!Y3W{?~%`J_Rj4aF&jf_*x6AdAQS&%q22AxW4glw6i0raYN$bB!y z=9Y=6#;KMm7KX-&CZO4Bgfv>EyHwJ0@BPtOHJfdU)kc(52K`an0u7^Rt{ zn5HBpCm9(hCa0Ky?qM%VO#{iI*-wL%m28+~nPg;?3R<$5WC1#@2Anp)mP3Z&(lV{A zu;dszB{Wl`q(nobM9V}&bK^ua(1&x;BwHk#85txdrkR_inVY#XfG0Uo z3krxK=AcW6K{>=SD6t%#Nt_|8kqj*obMuQzGOJQk;`36=VV77K85x=+85^3KS*E3e z*0;MdU@Ai-Tpe(3v9f|v?m78M&|x5ATmV`tkXlrh8V|cG4m5WiNU}&wvP?}fGdBYTDO8Slt1t^9B=ZdnObjiJ(u|GF zEiFt^Kzr;l%mNnweRWA!z$4A?pyy7m}3F z(|=;Jxv6<_qN$~!d6EhEG8gdLNc`W%K?vkI#PU=MwX@v?S2AC5fN| z_Rajfhz+f#}H>Gk-5gw zFe%a4A_cUWDcK?kI&%hb5B_9nl3ZE@TEHKl1Fq*lxBD0*nOK+^Cz+a=C#9u;h6+%X zP&xe3`U&P~N#>@e=Ai8l$p+@2rDI6_1mc1WrJOJ@H8L|wG%-ptOEpVOM3fW68-+P6 zVrr0NU}6B8pfpM{NH#FSeg+-3X#?cp1<00L%#DxWJ&2IeKya!8c?Z)CmMP|GhL&mO zNydq3$rk2G=xzX)gTz}08c_fZBq2{2!neN~KzGl&=9L*5Wfp@k14ztE2OnFMm~5J4 zX_AzdY-DI?keF-+x_up?fM{bak`oI`N{dqCGt=UeGhl{;)~r|J-rOAu|I5gCsLki{wOO<3!M1rl32(4Iu*@pf#G{ z#0Hw(fjdu659~fYz1+kCJw2=r1dkG+^wvRxi+XyHA#J#6pz(#0eAu`)$Y++|!5ENY z(8gqAL(4=1<5Xkg#3WM-&>C&{;0|fwWszANpPE}x0=mr0%-qn_(lE)$AT`y%C=oPA z0Fy*&2cS$sW#*+rr#`S{3-Bxnx_U?!fDUtcQYkdj3@ywok`0p$EkMVPn1V)lO7i1D zVFht{YF?R@RajzCu|L5bz^2LOX`p^enx$c~xoKJ|)*Og#9?q0%l9!*7YHsG2pOOk) z_KYXdfYgGLPEmGhk)EDsF6dBHXB9}&c2)_gEC3(aWeAx}1`maS&UrR7v`kDivM^3H zPBO7D01e-O6ADBg;(e?UL2|^wngB@I(aH*(-mR=aPPekkO$0gJ%E~7-G0ndSc83(W zCWS^g)b~V;VSz_Qv5jjcB^o8B8K)Q}r6eVT?sWoZZRqGTc-RziXM~;}B=yCE=MDAr zuusn78bCEQN=-92OEpPKGq+4OhhCm-23evFwg@~v3$Z~@54^J_GcO%8MdS2IDkzhs znVP2=rdnDUL67jqnUwGpdWJ?tsk!-Osqv|K&};cpKzEEJ8iQ^lN(A3Yj;sJ*q5xMg z*wYJcDR2yv<8U*Gvp|JyqG3{!NlFT6yO?DPX!08(H4tt!^G{ALEyzqwaRmh@sDls5 zgP^fTh^ykW%Rpl(riN+eiK&SOX=$cLrpCsgwk$XrvdhSGHWA^7$88v?z#!Gs$RZ^v z)i~AAz&Ht1NkCl>PA0T)BDlCSG&S@{%uC5h1%(_m!y}a$@H!GyX6Wf56^%~$MMe4L z;BpR8>=85oQt0UEp%yxz8dJqt1>z8}k?tWXrfK;_;2Yo#Op{V9jEqgq%^Da zDlUo7Nli?PPfbY&&B!O2TUr_#7+RQ`S%R+A1}(-wRYQlc#~+@Mv}ENo~q9`*xqXbgeq?uWynHX6Zn;M!WC#F~$x-t|&CFu}y#HU(N zE+itHjSMWv3umIk5HwjtW+?(KJBYT*8{G3i)Mch7smbP+$%d&0$!5tE*JVV6rilS4 zr9m5*SW8h@Dzr>2j?VyHg$!Dxm1b@Mx{M>y5H#&;0UEW0%MQ9Uh1Q(J+1LQ*V&cQY zEU`E_6?_e`d7_!6sfmR}nu)oArD=*8XhsPnHDp5q(q7B}*TnIqc`2zyX{9+i@t|54 zG~r>KYGGs!zLzS|BGtgal>w_t+BFgeRY7Z>lbQxCXOoQ0P16j`EDTZ&O$<^&XV^mJ z230~p>09B?42FX$q>-z%WD~Qb)MSH{L}Mc(Q&Z4(mO)viA;KD3$`YRvz~9CF|q$jXXva}?o3NUciPg@fNKIv0@q-|%JoR}A%n_2|!%A}em85x@xrCFM$fUfB>1g(dL z%F?X5B)&ysoRbPV!zI-+*(4>^*uc=(z!=1KWynd*qf06wt*<~*yKb1KE>l>_G6&sH zVQ!Xck!WC=Y+-@CA_~i>KDeoCZUQaIi7L||g@B$OY9@kp4h$iyGqY1GjZ+NGj6kQG zf(~JehbVyV8put}1BYeNlNP*2FXAin@qX&9Ipn;05fBpW3sCtD;YxiS>wL+>=h-jT*# z@Db6TFdCj+8bgEPlGMbUc+lt;Xt{E-QL2HliAhSDk%?&}Y)$;p;!iAkWdwaiT|l0a7+Vo^sp>r-CUgBBivT@6~GWdb^l z8??{PJUJ=VEYZR!%_7Mt8MK}gBtb9d;BR;sng`|QgLfezW~mGE3*vK9%ThrH$Qv42 zBpN4~nI{_=gBDICf^!Q*mePpDb;<&0=owUg8=9LMC7PO<7$l}8nS-{UfOb$rcI;u# zsCXJ-ns^2#bl@j5gA*Nr@EYJk%n}qiX+`;Ypcc7BYNA1+xrM2DGHB)jbXYH3mV(H^ z9{|I(Kr@7tSl}^aP@Xk4G&M{~vM@AFNwTyAoji;rPqV{>D z=(5@DGEf3AG|xyhvNSNVOiVMiFgG+Y2c07Y?qWmaX`Mh2!j#+r>b4%YmD0mZp}L#zscTDdrZ&iOCiQkPX5RX?!t6Q73fZd~a-! zoL^Lwnp_f}47$>)C^fG*z912@wIr^(@at<(hQ9f6BCWoK;wHz z@Y?=bf2e8RoT&+4o6G+=FKEEhFH3xh!o|#c%s)cEqNt(I2saY!M zl6!dRMN)KHn53Co7@Ap{q$Q`OC4ufz1V<4hx)Fz0P%-`zEiKc`EKN*}QVmQk3{#TO z;}6>bEyP4vVseUwSxTyfQKE^NA?)^BLqo`x3b3cJPjP^EczWhR_Gp3@U%*FSF?WE0 zHXDCu1r)Ra zlR(1~T<{?7XR&~7R5LV&9vTs!T9KFxvO3Me!X(ki#N61@BrP!sbWRH_?PAdms}GHn zlM;`1rv{zXhH=g& zB0RxvgP(PVyp9tp<5!vkJ?0TqEQ0+6JF<_sYz_;>WK)YYBNKCT0|R5wL0kslO^slW z;mX{ID_;!Ejg!(+jg3K}mSmP@4qCZR+NHdZz3B{)I3uRc18ab_6iC#Dh%;PXvxFC0 zpniHMoEcjX+~+F(>uXK3J`f4 z$h-$Jp$on{80=!$>9N>$S0kI0S&)i2^Uw}DRf2I&BPfkh)B9-0G$xy*nV6=5PGc}i zGfx7YxmuK;3px!czla3SgZ&LUy0I9V-;n)@>u?2dh=DcX^E)IzL81fe@lDt&OiRO5 z(A|#7DQRYw$!2Cq$q!toA{ETgG67nYK^lxdB`i$rrHBTLZjn3ky~$)ExqlHthkDb+I^YKk*6 zHcB$KOf;}aGdDI#O-XiT03Dc3a*6{d9&oawes)8NSEDr4TMnVMosmf@=n#j*#AMSn zGjmJGSrybdt6g$GF+sJEDOdnh{iKg?1(v}d#~9}s zfD0>p=gp$tQ0pF|0=aiJ)x*u>l@33R`@A!t1s=rmWzDXLH(q8_0OIsYEx?9yZ$ z=9`#UfDS1$OEff1HZn0!1+@vmH=2TzDy`CkNurUFWvWGDs&S&Jpg@Ds0Shb!WST!gcffa+|7o3$)4vYa$ z1rc!yEB_8<*(r8wC0;GzxGXRrhhfPnIvJvRI9mpbUoCyP*O3AYoy4>2*!YC=tz%VtRn+WPpmnQ^ z(oB+5jMCB!Ow3b2H&%g~2_^aQkjCub=vh$H_o!`LQ!^8@cWP(~nahT!G0-KT#re6Q(?2YdjgyQ`5-kla4N?t^ zlPtl<$${i-N{x-cMI%Z>89sJm4DHazgZ*J<2Cg*WB{cXDCZszGp^EkNpp5wB{M_99 zJUu<<{M_8cycAGvYG_!HUz}MHUtEw_4%#PYU}Tz_W}1|0kdkU?nPvbv{2aSuaE;X) zrRJ5z7blj3ui`N@&oHqxHa1Q)GqbR;uuL*bGIwP_Rsiem5juzt)EF&Gg`JuQGdnHK z$UH64z|<_&EX6E2*&MWOA59g=0>}V8W{Va1v^UdKV*^ujb0bs3RMRv|P$``WJqivs z(gtp?Sy?&dSJ)s90tLqwHPbsz4=f2BMV{d3 zfN^eWt_kQi4A6CD$tDJ-DW-{u#)d}bmf-eu5p10~@}LBY4dCFxo_?sGm`%Z{*gQEu zHz_j@l+TP(K$kNcnu3m{GD-nmkPMXrB?=T*;kh6x%_7yn(9|H+5Oi*ok)j&h7I(NTa8+Ji6H$pn=J zt`!A2naPxfosSUI@g(OlAK~@oNQ^3WN2oToM;A_>m<*~I?%v{QkfRW<4&-~ z0jLR#Ic#obm}-(}Y;0zll5CukWRQaO3`FEC0I6OOhn|9qYb&ejYEW5STZ@titgHe+ zC2%mP08T6^DMADecE=bQ8l+vxFLs0o|VJ z2uYh@H=*PgTuIIxTDKS)rl*#~7Zl~E7bWI`Rv)LBq@@^H8k;8>CZ<`K8G|zsOdgb= zaTU>sL4{-^)5Jsrb0cG8bIVjS(7DBl#h{J~VtNfeSr5)RusFq)#!!aUQ_RvV(h}1w zOq0#hl9E%6k;XM`V5Xr20Qd?b3)sCn@B_QkOj469j14VK3{z8$Qj9@KB|kp3A{Ec5 zCiTlf^Aw1k1jj}#jV%%rEeujk5>pb5Qq0r9eL)m&!fzr?GEPZJGf6W_Ni#D@0hj); z$uNu=o&ofbfq9}?vZJ#$90&_`cnq1RB^xH27#XA_nu3nOO><>PE-Q&I0wrH~Q3q*U z(&p-!RLevIBeP^9OUtw*Q%f^L%ewu4)p@Qb#2FwTozIk(QQXkdkI;VUlES2wFmi)J0T;%tK+F3k1hB_Da_z#mv~m z($v@t)BrF9ZQVkxbV2DBTQ7@GTl zDDTutP-uaN(JV@eD#16df_4;{nOc~drzRPiCZ}1Nf^JiSNkVrgC#UA*fGkLJPR+@I zia~n)sQ2e0j$XF1g4iFQnU|JtWfcN;jAve2zMUPsa5jLCmV?&7!%c#vEwmwY*zh^L zZHC-~HZ(-C&d>m1oe5}_7icp`O0v17siARlQff+SqNxdF`4D!m5O4s({X{s$u(%{K zIXgbRG_eTOA2BjAPBb>POf)q}F*ZxJKr}#z_YA>Cj%iY|X=D^~NC|Q@A4BJ@Kql$wL6U)<9@^j!W+uXU40f7Da^%|L?SXZ1{0yYO~45-&)Ze(g~X=-L{k_Kw7;8YC?L~uI5UQ=2o zrJ5%ir6#7Cnxz<7n1L=JEXprHbWbr`f|w$1dH#9Hso*ws*;CZ?%omPRR%!=pe3AZHL*z#0{oBo>v#Lm~upjbgH4qPdZwnPHld z1*nhU%7Cl@x@fTWOZyR$6`@_)zZTM9|E&QDTyrnTbKN z39Ok0k_ENXz&4{bsX+!pf(_(ENGv7i78n`?Bo>uqCg%7Tr5J(FY%@1AG6Qw{l2Z(g zEiKF}K;3bSkpsxZ^&y$Lsl^4U$&iK(Cr~kpk@TPn1rvB2y%nm9SCu=v5}Ewnt@TOu~D*_sigrVLcung6j$aYnr9JF@{2$@(%d4=(jdh+)zH*3+0+uWKLMr>G?;2um0FY! zzEZ}_B-y~gzyx&ulu2R|=*k|5lnpWE2-uO3AO|T2Uy5vK03Klkk2acundZ5P*{Lb1 zptJc+lFST}Qj*O~5)DlZj8Y6hBWYkcctWuR#ZG);QF4ZHg@u`EazSZkilK3QJeU!m zoL>sw?PXz+XlZOvK^Nr|gA9ab+LDxbh@=^WGRiMDG=(tZVaf?sYbF+H$p*<5CP`_=#s;R) zDR&$e7$;?xn3SXzgJuIQj4cc;O-w<@A*Yz6fOgMAhd)5)hM+mjG&wOjBh|b(wIn_% z6QnuC(#$-`($w70)W9@3B{kKR0V;>bcMwAi4L$QRK{+?GDz(TMa%vgqifcnd<22AZ zuVnD`k+1{?y}r=^QZ#|GAUHpPA{I55erJkN+9(cv5c}ZnKYJ4Ur zn;N7UnWq{WSf-kQE}Jqmc4dId!TQU^p!0*$t*nB(Y>82laiXb(rD>{Ba;l|~ zc{0Kh4NdY)v@oztG)gm0F-tVI1f3LvFcB2$R#pXxd6~(`Bfqe_N8`b3DL^|-@{6pj z{PF`5D?tSod9FxIF|af?Ge}KN1)UoMTGtH=Jhfujh%#hQ0aVgriIlY563`N=q|$Uy zDQujU3re;I#-^#smKH{7MybYTmZmAL3~9O0r2%=VC016M1y)v`1!X3VDJjrVYclLG z2W^y0vNSX?F*Y(V1>J)Hvje@4?wy&J0uCPN{SG8~1$5bcnlb3`3!`L96Eh^QK&r9e z%HopLTu@y{wiT(S24-m{sfI=dphZRI5Gx?50MaRd_vj$4JaR2EG&40YH#AByOEoeu zOaYBWfT9K39Kd!sF!B4-u(}pJi~(M8fw6M|GD1N{d>ERWnI@VgCmWlYTUr{JK?4hH z6H*H!zu3ykD?c+YAh9H)7_zYdRAQ22pGB&%X=^{aZ*x> zVVbFliJ^s&Dd@%|kPYBqM9)2BTVZZ!o|KZBY?x-8Y6d=o6=Vf$N`#E4FtSLoFgHv# zH%>J(wKO+Ei+bo91uLuak~9k|tMHOE3vyi!Do-p7Elds5j8e@~EFcX(G8}AdlA4@o znQUodnQUlkW@rIPJCJBZ%h=$M$B|XYOu5D;X{IKIiAJdw7UpIK7KRYp!4^T6by!)! zXL<9;4y!~Xa}y(zWYc8hB$HIoRt-=rLn}pc!YnP(!qmjvBGtsw$Se`G2M1(cG}zf# z!zY$pmzx-x8e66$rly&vSy~u_mH~on0$YR;KKZ33WLslqW|*92W@u)XoMvchVTl|K zi6t4JB}J)enHA3Yxdr)osd?nvV_|6y8a6jfO-nXNGB<_T14(RorMaM%LS9sAQGTeI z3E5srNi_gn!fR%bW@?xM8rcDb7{m&+^b9E=3m_g$EXg3dyf;lsHMg)dO)*JHGcYwv zhNfbO@!;~dB)P!KDkQmp(lXaH)dF-!U7Dd0=pqHs>^R7C;N*Z*v7{A)>f+Qq$RHi` z4n)!$FJ{RpX_hHQpo{mCEE7$UTNOx_!IL4B0a?6dW#w9tSrS~5SW-&9{V6G_sfLEe zmMJNwW=RInM2R&fq0IxdMl`ue$lSohJlV`L$-*Ql(Gcu$P#1*s05nfAF-%D_H#as( zOEpR{G=ZdcaBL+dCTEuvB_^j@Svggfq!#-Zg=eOulIvg#!$jlMWD~<=(8BLD&^61T zWC2@jM@9i>VU}u^WC^-oGbu5}47RI?^boKBO){97C0ZmITBanWKr;q71i<+sF(swQ z$|^WNIXkrk)XokwBRdF^O)X3e4N?qEEG&(ZEg_5E$?&-)=!!VA6tg4)BNHP7!&FF| zLDLuJW>nIH#L_e^#lXzOBH17{*~ApI;~o@O;2?qKAViIqT10m1%+kWxD9Jp@&?4C~ z4RU%kd1=@(&D7Gs%pln?5p*-ICA4{vl%JnNZcHU68Jig$S|q0?nIxfvFnBr` zvIr5F%6Qeu+2(h zD9A#W%p#}K%$yYR`(DYW$tFhTriq5e#z`qjpwWL&dcfwnpwz?^mrSyYkraazV-wJV zqZ9)Jb2HF<2iziPyQv7&FeEq3QY=zU6O+tSlM@XMlaoR94BU8dI~ufKp~%V#v^xo0 zO@?QdWWYAoQZitYYG4i;$uLbdOS4S00Ij@)+lv-gpu_<>hK^hxrkWWT8CsZ|7$uq} z8z+MXM?p405_(2rafX#uF{mgC&MfdqEY3(RGBhUJrqpCJ<1}+avy>!rQ)AHD1dvVO zG=bdOfHgdVD|3^`FT~S~%uP(v3{otN5>rj!o&Qquo9$`FiKga8MutWvW+`Syh6a#Y z0qlD0Ed{bW2x(@ipwUrK_uMkc*aT`*CIwDUwlGUdHZ)2~G_f#FHbHLWASYtz5@AU6 zLN?gq9plR^F33p)k0V0|EX_>Kj1!H_QVb1EERsy2i{(tnG0EJ(*vKN;!ob+V+|tn0 z+yXqNO_oU(CdQUVp#2G!=B9>8pu>nji4Kyyp@YMqwJ4xE5aMyjelL6;4=pH2E%HdM zKpLtww=hVwG&V6bO0={zGewzn$}cFkfUOKOa?3LTO)-L2^P8EOm>QWI8XB8grlf*K zH9!(JX?YrID2@vN4Pb*7s%Yxqlz})Lq|F%FHdF9YGL)5P7Ut%b$;K%rpj{2d$)LlO zKnK0$mX=V}Vl&7DvWWr6OYxuyQl1f=gVp2}3UU5cdu1cwSiVB!8G|w+tDalF325FX5^o?<%X_`T*fq{jAL8_rCcm^=a4?GQv68Yo=l&N8ok&%Ik zd7@!bVp5_3WamG1jJ7aNN;FS4F-SH_HMU4jqGt3a8k!kfB%7ur8k!m!CZ>U=M5z@Z zNv5EqM^i1*(hSTEjX_&(Kz$Udq>vPILrY6jGcyBAbBp8@6KDxX6{FKELB)}|k(r69 zv8j?HEW&G%!msNj5f0!&vG9uA7mU02btD7U$=IHhh3+ zuqCiX7kSB6R=K4msTJUBT2d?AK?~%NC6f}1iZVfa7o8vs(8y6L^d=@e0brP%XkwUb zoRVgmm}qDQ+8~D-0AM$Q<_f}#GD}jetja-Dd|Gaam6c;kVnIoM5yTN7CB-EvAS$>d zCAFx?CqFqGIvfa2G}uEZ)yO#2*aB1tnHnUSn&Md9iSBubm7ql(R#rIt4es;cw%Ewf zA{kV(SsI&zuN1&)acWT!XzmrUD+jbr7&gHSjvYcNz{oHOw1MBqz|17o*bH>_5^Cr| zM@3+xZlG2=TDJ|47mUmjO-+(4%~H(`OpKB-R>nfjPRcJT$}hLFa?US@1RJ#1ff%jD zV})^Aie<7I(Zn>-z{JwXD9so& z=YZ-PsKas2kirhHgE$=AFh!s6gzl%rDn~Fgm>QZTrdpa?q@|`98G;WkCCz%j863^qd3@Rq((cJOrVFNMluy78zu18bK?~O%u(N zQb32d7^fzICMZw?3acNXRzlJtQnD~@w zQbNR;JX4E`h)tgs=0>R|i3UkVhDnLZmdO}}A;epdZTz6c<)9QDQE1 zG76MdurGy9GfGZOGqkWUOSUjHGcip@k3O*JNTX@+*$pTIUUE4WrI!)P-f4-(mWGyT zpu}RHVrqg>xm~oFJetB&=Ll#oF>7{54xbxEGf;@)Wj^!G{q3Z*HDMxtcHoI z=D7vyA?z;8_O2~i0Ejnr6zwj0KS5))|j#n>n%$->wmDaj<&zzmc$ z;2pc-k|MK`qQuOSVnYMSSP=80K&?>F@=x$8G_WT4iEjj#&!$>fCZ<>#Sr{3nrKOo0BCo*)?Zt#T+ycY~9qDTU zI%EgLFf=THtU!+kEk`%cFi5sE0=3jk6U|dh4GmLV8Ia_WD?Rvviekw6b4XQ)5JiN& z3CL2M4uC9GH8C+ZG&VFyO*6H$NHPVT8J${43)_q#zD!FoH83?aH!(L#NiC>sfndQVq%&}ih*&mp&@9)5mXLTXP83P@Po!9b4ww60mH$m3}sO- z*v;U25R{+{4ItgfwWX7KWhn#X*ZZKpPDZ-odjPnF=;rfR@%~u&IzCB#KQnH!?~zPPH&HHaE5e zwIiwH)+8g)sQ{+NhDNEzhN%{i)ejWAHOV5y+%hrAGA+eCIms*)GO9?isVOOz7O7^5 zX3558DV9d5kcEpBm|9!{&V@nWR}7rkI$TftP;9NBKcqiFu-!p&9s$bMVLq z73@pqHlF~qX=RjvoLw4X%!+i4;Gc(Y6&z4DMDV8bF z2|X&hKQSpeE!E7>G||i;H6_si(u|^_`AMdR#ulb&h8Bi~DMm@qLoK13lOf>)8Fd98 zcv6~E0x5N!Ra`-x862G*^f)j|HZV>~GPE=_FitWz0(C7x>pZ}vE@ZC{Vuu-|Ckv{1 zQ3h~98I1P3%1z7-jg3r_Q;kdwQp_wtf9Tnv_x~mL}T+* zGtgK()S;lw(I|$Pq$L)Y7#blTo?~bN+CQHO->M91qof#_S{Rumfz}wAn3$!yGN3Af z#vo*j9Xz_OQEFzQqflyQqG_j-mZyOfh1eB=BZ^GdnVMLDPWUlOOG&mgH3OY@fyZ@0 zsd&!$0oUVJR^SF7WLY-24`pYE-4kS*MTuX{Km)O6#>UCUiODGjpaEHINrSjpO*Swx zH#RadO|mdZGBpP+3ne$)z$GK{;s{7K!*)Irw#0xp9Emp@U$`2lq!yPHsF4}|+tjFSuv5>t~5Es_#bEloiq+sHm3-hH^! zGxXkucL zWRPf-l4NcIT|!ordpVon}Tj6LRNsL+X8y_R|@D@zEU$2kp5%~qa@SR zG;{nv|GgXpsUspBbFc%piM> zz@~!cS-{)J^z@uy3%ZFkJ2lB9G1FI&CKj`U!Z`O24O-n4zDS>Q(z)WT-5NXgE9$zi;Ws-%dC1^!We3T#bjv&0&1|{aD7 zkVccT#3E3PCt8|XCM72(8d(^pm>YmLi$LQ9q{tkkC_W#g&e$x?+$71wI4#-4A_aT} zFjNj?DD>!2u*XyL%JlTY5{rs4y$9-c5POYET5?K~xdG@ZS>seolVr%cR4UqI399+x zGcv(vFqxVqo13Q^8zv_kn_DJ=j#UK>NE(9A+9KpBtZ|lNkeq61lA3C6X>693VhNpa zfSe+PYM?)saKdV2qM2oqQL>?tp+!=%nUOJMz6F*H%;U=viy#q|mY8gsYH4C>X^?D` zXl@Ff@q%2Fg<>_hcZL~B5X&+0u4$^dxv^QAL6U`8nxzS-@&N@AQmq4vcMM}8mV#D> z>ghowLDwf6rlb}p7iAWJMrlBun`Fa8(3nq>QA&!b1?W-+q-1Cj4?5kc3_SH|WN2w> zYLa4+lxAdTlng3zu_+{2Y$TZ^8=9CWC8eernWUOQLkSj|hKBJ)sk!-Osi05=-M5jN zVrGiqEkXdU;K!LIkxC0J8 zqXjd5NXbMgsRo9YiKzz3NhXPjmZ1HCpi+w3wxMJt^Hk#`OABMri6myJmIe?HnxZE^ zd>(`(K(qlllO#h+P%9$M%*@a_cu0~4l|{sOG$p?@ zDJL~PH7_MKMNiKK%yR{CkUfjGTq`NrBFW4Ybn1mksxhSDZHivwVz(QTi_r>*G)q&% zv=kFV^EBgBi$o)+f!LA=(KW7-iD4S3xHm|#OiE5of*1+PGN#~BW>auwhIHVbX{v=; zT8c$#ih+5GnF%PB!p@9?jGKbTMnj4!LCeM9qobf4gU3vB%QTB*votgFM00ba6wn4t zn3>=q8L&f;55vR0;R25ZspiIpW~pYNdmB^Dj0`Q&kJ|$e2%~PXKsOE1PXQhBX=Gq& zW|ER>VwsX=U}k9O$^h?yK_=M9bTW!1Nhu~4mWHXIY-46_Y+-_I2_&pZb#{tjijjG; znUP_VxsgG#X%eQ1M8}?Ka$;#QsK_-jO*1wyHn&JNPBS+HpO*`kfsPuHVV;?>QKDgr ziJ6IonPC!WQU_ulB`E>!*kl7U3sbYClq6HLFGh%Dd_1z)+vArLvVEsTbl<}1s(*3U)7LOnFm$`DV!iy zf-|8?sZo*&$fcmZT~T&wk)B>~WnMBi;Z(>iZ6FKv^gy`<<^-5Q1}e}w7ff5Qv|E#n z4O3E04H8Yw5))05pr@D`LH9Kyn*j3qun8JSP(#XLjMkJ{GH4;Nk!7l7no+W;Ii%P$ zf}V>rB7y|1YO*vjG)uEgN;Xe50-e4CS=EOqL=6qGE=tD8IG_ci*cK&|m{r_ER6vQ} z1eEqcSE*Vgn;M&%8>X2WCnuX2VK~kh(sU;>=|c9-f$l>r2PJrzb3t~3W;4mm-5~Am zAu2E@o2MiuSy&iY8k?pWCK-XYpn@jnA*B%{Wfqqd>FI%IA)WH`bI8ltX$Iz|=H@2m zi3W*DDF%j+0SHq_>j=pfc-06lnMf)+OpMbKEey;IO^gjpO+hEPfxH13afMirSDFj; z2CTV2vLz`NX{Jerpqq}2jm<2f1Ds}%rX`XkXsr^Gt+7l=OEOBcFg3L_FiA0m?%0No z@ItIfEGkN@)YF4n1YT-SUc8uES|+9@Cnj2&rkJM~LR)3ffgy-h;9yHhO*Tr)DS?Kc zTTXr=WICE;uO%8K8XBcoCMG5tnwl7;Kr#q)DFrm^!Rj(l6-PmONKQ3KF*Hj_PDwOQ zwgk;$fYJjb`(d}p(2Tqw1g%#vPf9j20A0qA3_80FWD{}*f)_rXd1;9`Ihjd0sU!!S zd6JQ(sZol7g^{_TNh)al2B_19ob*5|4D|F;Qj_yjQb7Z)AUd=pja+Y;n46}WTUw+T z85$=hn?V<)m?B#UuCd4qvlL5E>6mPgVxDShXl?|VAv1$?SRly`G^Y+~suUIFmy>5l zvPoiUl2Kw>VoFl7WeVs3F;Ki9djQ!E-%>JDbW&<+qLF24Vv3nrin(zTWHX^Tq!NO9 zB_pv&PY<35oHG)Opc$7uk6Bt6TcjG8Sf-gLnHU*ZLdHH&@+>4Nz-H4P1uQ_PZ$T^TTy!Fq|9 zhC<9NN(8N9O*TxkOiMC1HcCu0H83>=9k5oEpBrD4SdLr2A>?*kND*deo?)42X_B0l zlw@g?Y-nbggfz^I=^snTqFc~mlHi?O@p+{=AR`PwR}om4m>QcWC8nhrLa)mXeraVVIT(icyH&`4y=t@nA)G3^V|%1>fcca-OA$325Rb z(ZT>U-3=+wp%pv0d774%siy}vQ%?^Rb;u#P zF(-_X6FfFsAjt-jp)k6VmIg`2=82}}sTRg2=9bV&K-5q)G=S8PXfcbC4#B>`>KOEN z2v(VyucwFZcUW>WGPg)hOtVNcPclzRPD_DYv4%YNi);-jA7Gp#h#bs#10XTU%rM2m zBFPwZ*R%cHpIeuC#GwfJlM8 z25In-VjJH0O-{0~G&E02OiD6NG%zxS)J3TAYXYi*3{6Y&;~^0mpOar)TnSnZlxk#| zVr-dg1{zdIgm#0m={1H#H&j2U#bIu4W@>3-VVG=boRS9G*@2=0DBGPEkrB?1CDzHt!CMK36ycy(#8w>wpNS1^y z_A*Q{NK7#{H%~D(F*Y;;ojQtCMWO34GzQ%%30h~An4DPxitFU$v?K#Vixkjxz@~=D zNNYgS(lXJtTO=nIz%*JKCR-#LC7W8Nm?l}6K*uL>xfQhNCbg&xG;A+W@ed~ zoSb5q0=mr-uX~F#iZb)E<4f}6(=tI9-6Wf(q*J`(_% zxGj<_6OE0M6O)n+3{sLIORS(b4dXEr)WksPZJ=cypAZ#DN(5Vl$V-Wqsfm`L?QUkt z=EfGF-KLl^VhI^NqNbOT+ye2od9qPTnxS!;v8kDv}t7RG62<|&{KtARxdbn7lw4TdJ*QVbG_nR)4;Ra(Y| zrl!WJ=4Od$md3`QWP+*$RMMfDWoQg(BB6&N`ZNY4TwpDFh^5e!42m;wQpP*ni6e=C zH6s-^$p$9oW+q9AiAE_#C<}T)T|sEhH#Ei`@91vEVI`=CfS%H!15T*(FF5q0JK4y{ zFfr92%^=y>IMFf09N-H+Bgzg84hise#uPgvn=b+Y*nOUM`nt`#g5mJ}k zD6QB8d_^~6{%E*TQv=XJrpcgrjxjU362wkCJZee5yUTkBTW^4qyq5-rb zEhjY(T@U!$OYrT&pmk%G=BB2}CdLNFCZ@@0W(JTQJ78ImKInFJaI#4&*3$z85V+hx zsU9F*BeW?clho7{a|82agH*#bBLf3S{c3{T1j1q-*dkEJ2DE__loSn(L8${YAO*T< z6Le>wg;`3XrIC?ws$nu{ogrk>!vs>1g0*9FCs+bxE_j~~*kBWIxU2~%(`0%bJ|&<103i`2B_WD`R}NGFjj6ES1S zD9PNwA}I}Y1gnvyIdsm>6jBEe=URA7K{7C?5CILYr6yaZq^1~~nM0cwf zC4&ZoER78fKudlQ;RaW0Xjp)_RKw6bBh@S^&CJp`(KOY}BFO@@Zyt*#b4Z|pBF88t zE!Eh-+|)AF)WQfew!t zTUeNw8X6f`7$zH-TY`?YMueOpl5)$U)a25l;!N-ug+ZdRWwJ$*xrt#)s*!;yq|(M> zhj~0Chl5&V#>wUei3Vv&mX@Gv9Z-u>Bzp`^AX-2xt)cDB6hjlUB;&+13sVaV==BcZ z9b7OakcH0q%HIguj|ER7|g@szEI5Cb(^K-dtyXf-l7OSLpJO)@nCZRatE zOtON5BQxIu(ql+XF|=?hO#|2dMWAdO4l>CS+@=C`DnO%(=E>%%#wMnzsYZsDhM?iz-2*U0*w`@PD2a0Byx+{%);2%GBL?CH6;nuo3=nQ1Fj5Y zm?89ba(D52@4w6ue-J-r2<-0Y=7# zX(`5*NvReVN#s;(s&HwkY!?i)2H?RLdmLfzPl4G&3J%xq5nPNqkXi z8t8r&1A{b!6m#RmG*csEBl9Fz2B;h;Q9)BEhE_-`E;TjP*wDzx*v!%-*)q}G7@{?| zv;>D%L)01g)I>`oGvicCBaD6f!QBWMH0Xl4xLNoNSR|kOsPV8XPd7Rt%Cu%+Rt0 zxJCypg$Lb45|mgDjwR?g6k23~&Nc?O2@K6M%q$H|lZ=ea64MMoZ3{?S6BhGeGfhDx zXp>WNQi^G!QKDsXlBH=%3N+KBDh)14$;`6^wb|oyGV{RGP$rg!iOGqHDamGLCg1}Z zkL*Xn@M5Xgpnu-maM=EB-3OEWgNNK3X%N;9`KOfyb}w3SSe8gKZ_g@*@h91S)c zkY-|*WR#q206G`T(h@Y@2XZaBreco!7$+rKS|l5RZpKLgZLkE-s*&w#Lql*#m*mGo zMkdV7lataCQ$XEwi)16vs!$|(aA`rdPrw0$7+MB3kPH(|QY}D-Hh^aGq4gE{UNHbW z6};aL)DHtSzAO@r%@RRldZ5Wxgf!SAXeBpt0s@`Zi8b+~N4BA{nW0&lnWagRabltw z=qdqFWK&`?W`w7in;Th}TNr?DkWVy&c9G4{8r1k)kEgawG)qcNHcL)RG_^1=H!+4} z0hIh`R$P*j4<5BiN=~s%wlFkGwlFp@HnxOhbM(ps;f{RhvDVP?1CJw;EiKKGl9N)6 z3=GT+OpPJSq|jReI1PqpL~x<60xFomWgTeslZBayv1N)eXi6o;06IBKfx%dc{UkGE z^Tb4RBNNl4v@}C==m;;;=o)Gnf)*%P+zu;LlPyh*EzOK9Qj9H44AP7sZCMK3j#51s zn;M!KnkQKpq#34~nwdj-z?2&fIlns1GS$Gq#4;txFv%i03EEDAE|P|O7$rJ!W;e_z zO$1FhrzM*wr6!tKq$H()OFs$?#}ch3#%U&IhABy@sVOE&24;}PDe_KpjA%vo1D^D5 zXl!O-V4R$4Y++$+W}b%9j|A70`JhafS_C@EBiYE<#KOqZ2-Gl4G)sdx1b3!OEh4s^ zoSJN!m}+jBXk=h)W@ceYDAS=C439_57)misN-{MtPfJNLOR-2bCX^!x8jK}&49rta zjFJtKQw=N)ERvvy<={@f1g(J2CZwjAC#EEW4zo-#OEQ8MhZGo$lIfCDlhRU*4Gj%b zO^wYAOd*SQDK{LF=?qPiEGz{P8k<`hnVXoIrX{8rC8G5Nz!@K+ya;mcN}`F0rD>{R zqH%Is3TV*)I7_3LZWdnvRhyJ%Xq0G>Y><|mVqgM0QUyh=d3-8NaVkh*qLHzwNvf$? zGN^X|l>=A*Xk~P1NoJ0oo@;JFNhOh?Vr*$*VV;y~k(QWZU}$89SR`+hnQsCz&a5c4 zAT<#*;bER+U}>CemY8asVw7YIx|ajgOh6j`2NfqEEuf=PL3)UEf`N%es(EUn3F!1A z3!_v>l7a>Y*a;@ZnR%e8O=AmlGlP^w%cMjz&}unTR|b#-I2@2RL?B!doSBzRq#F!V z%+1WqjgwMAdk9m(p$u7_YLuCe-nvXqH8Dy_O|nciF*33+g*IiN?nPIdYLsM5)@{Tbvf|j$1@+iiHu06nweQx7=jLWPc|_&gk7@&wcZq5lA&Z+bMw?B zQ&WRfGXo3Lq(smOkKl#{xDy55ZjCew8VqhFfhqx%ZYQRpiRLM3Mk!{7NlB?GDXE~t zwa^R&ov;j=#UW%)l954*k!7k;nt_p_3FyjBG;?rXDgcUg+@4M`Oi4{nwKPvMPBS)7 z0WCX6GZ)p<(1HY~Vn`GS-yB3Yk}5H1qZ609^42$ zJ?Nd9puueLvFEI^lxrWl%}q*|CIr@+P(aJVcVdd&bRf@$nBOB2I1V-pKwGth8bB4|Y^ zT9^%Fgr=l`E_F{#PE1WSH%~Kz55<6j4RKinD3n0iz&J0p#3V5#rO41AI6pZ%wZt(c zrN}6=0JO$E**M9}IMv89%`7P;(Gt>>!Kud#R0o3fSsI#}CL5WXCK@IinVKa++U_{@ znP(QqXBL!!j*v7rP6G`^SQr?lr5YrgnYl7RiFryQ=#Wj&?YSl} z1I#cDFhck?%)|oZ_JVxyT@T4-7O6%?iI!%@$;k#usfa00L`Wg)HAB{Go@|h8U~H0R zZk!C-nGM-hi0X4xy=gi5<(YYD`G)2hDJF)7rUpg^=4qyeNlDPg7aqNk$z3D^l8n=m z%}r83foNfo3OYp>BiV4ILFjCENEi)BV!I@iH zTACUfq=2>+nwun}R`EzW4Ks`5bMo^GK&N+s+C%0hiKa>BDQT9bMrnqi$ztgIGAQ98 zv{_~r$EWAyCnbVbXq%^47+RVdCYc$g8JU?wH$=l~b%Y*6qs-#?+|rzq%w*6t2<91S z$;L(|1}P>fCZILLNXsx$?J_jWERIjfPcF?(%_~VP$;=1MGNqcCm?frIS{fu77^IjY zpCg)=T7s|>+HL|hyo^j!jg3-MQxi=L%q>ljmL8$n2Gs#-X_**X8XFi}8l)yB8=07c zHteH@g@tE9nF%QO8ybTG5wtN1eA5MJ^|?t(Qc{|EiZSTU%tX+_2^1Be<^fUw!3{zQ z)}$megGBRGQ*+Rgatr7_23)#|GZKry@t$OqW^R(0l$d6ol4_I$I$b2QI36O4%T^<> zpHoZH;tPuMOY)QRbK(<=i!;;nKmiW&Oq#i|xupr{DCgu9bI{60P?HshdR&$mT9o92 z+Q4R@yLk%|OF&1BfL3lAnJU{4$SCBYRd9YpD#k$*)X;JpQVrp#N>jnx89;?IXk(fQr1=Ofju52~ zK7;WzS0LITNefg#fH16yU|?o!VUm_?kZ6$zx)u#GB1L73%|K}yWGM*4EjBeaHZij_ zNVPObN=$;?WCWS+NB1$*>BI(RqD5*V=)OB6BU1xI6VO%-w1qVUV+nI>9S$c!S1>}F z5*CSRsYWJdsYb>ImPyH=t8;MMi_1%dZAK1Eh`ni+M#dINX{jdW<`&7x(2X|GEq&4oB%b697lj%kHZPc zsYd1|CKjnC$wo#7$)L4G;I(CtmI`_>V7dXU0=c&YUAGPzAi~vG%FF||l0eBCgpt>= znx>h8HXs@&nHm{eCMKiyYK<}rpw~-*)|h}BdXUnAluQf?KiJxrQez{K17LZ}$k-$~ z*~rK|G1=6@FcGwh5R?#Mji(enJ){hP84@_00&WqNS|F}HHi0yPpwR)h+sq`<)HD^e zQZ~^%5qj(uS}g-E5R5=a%9iGVD{s)UjqG@EWe47Eo(viRH%kKDKWd(AW@u@gW&xU? z05?i-XaJSOSgipyNHDDdEhR8AGfXryO))V~PD?d3O#=^nV5%g_;Nr}ZREYaQBPzzJ zX=$m+CPqnV$wn!lsZu0|BUIut*br2G!rTFG6q%(OrC6GoB&8;%S(=%EjzU5*6ru{3 zf##4p3)E-<-F0A+Vqt7F(`AkT>+rx0*-fm10lRhgNEWuj3^vQd($ zxq*=d?EDZM1v@E58>Quz#21$)fp1+0oyTLEmSS#bkdkU(o@!=dkmSmMtN;`kBzg$k z>BQCxHBCygNHR!ENj5b%N-}}2kVamqNknKN7qLkD$B}d*7jBS(-7L{45p;B1QgU*N zu{m@b3J!b0xM- zK(k3mbAU*32MtB+P5{}BIEBK{JTWOT(b(KH$v7zuc60~k&=9oIhT%10>#4+?%=A1x zz05o_oOKlF%A%AMgG6HsV-wR9W60W3*ts}hd+_=YtOB{l#5ul*!!2kdcWIzLxv6E6 zrBSM(r8)HAOynXRvY(3NDiacfphyNq54_4WGy<<)ht*r4>nW0xK=aQjNyY|=kO^yZ zh?QWU;tdNNb!I8l66DG`#Vj$=!aO-GDJ9uF$tV%B!v%V~BiR-~DrZA8MD1*WwRSc% zHa1JONHIxHvao<0Ee&cJpbw5h(jGW(fPxIQL_t3s0Ae*#y8z@0(C7###E}jpNHjAt zOHEC)NJ%wGHG!oz(h3#qR)g#_PJ)ca;PADjxgqF0t)#>hgJeVG3lH*AOUw#Dw@wzB zfsYO>GK4HS1Wm4h^9`t;F)&IqOHMOLF)}kUGzU$;B6Zu*bVC-QW)_q|a$P(qu|j95 z(~I&;3qWIWmIlT~sY$6ODanSGhAGgQeONk2GY{?>had_9BAYM%>h`901aiBgI3I2nwTXT8K)R0 zL2q?I_XA{z6BGpC#segefRES(l@!4xkWoBXhD1;M;N<5Xq5?Un5HjzYnqp#^l45R= zmY8a8YzaLz49!||NC{24wV<(5Jw3!2A7oS?*?P-l^VGCN0}DgLWD|39&;&YWN}!(g z;Ng4}>n+k!%}hXtxmY9{874#4UqOcp5upc3&Y%hIlp=7N#@JW@i3l7c5f+K&smW$W zMn(pyre=ng&~gwmFiMIgV6}vHFBqpKrI@6FvX`l4szEBGp#h6e}8StOgL8k-uWLS~h47+{2Ed8%;= zXgVm#!pt<$!T>brhLm>Eg4!6};#4yO!xRgn#54m#3*%Ji?L*itHYtFdj9`+Mlx${d znV4#sWMOWSg4eBvpsCKh{FKz3M9^Mc%Oubdz$Qi}pd08?Odv@RyKUwOU4~|<$rhI8 z7RCl<1_tKF5M9Vi{=pS^NpgXn9z0Qj%Oz;_22QY`?U&EZE0cxx>VA_&@wgI z%ow^H7`ncXWWzDto@knynrv=tnw*?$U||V8kqM*6g*#Y~OChvVN|Mct4NOc^jEpP| z%~Qa$@@Q$*&@d#qz$de~BsDL!$j|^XZ~!X5LEEp*Kx>?mOpMdajnYyqAZyN%QxA$} zlyg^-EKHIu%#A^pUmF{yB|_FXXx;dG@T zICFt^!N-?@YHrIklVs4wfFz?7i!=l1Vb?fyn?8<`k`&i_tLvOt{x!tkV_ zK}lkED*WijBm<+wWJ{AI@D+2gnP{B08k#~1guJAj{N!xNkz9r*mgdQcNtWg*21X{Z zy?@B3rXjM4XI^rCF4}AvB(;%pKy#v5B4~a)Eh#O<$S4hZ1|i(Zr6~oLp(zEB)&gjE zd09N@>?K3<3{wM>L<_UjWYg3nGfP9HQ#U}ZD`QlBpt2IC&)6*4B*nne)H2y9IVBZo zpA)7&Lt{|*Bo*Z+ra-5@%u|z6Elg5OEG5F$HuNs&NXWABo*^Lvw5fSQ;c5C0Zt?nj5AW z8<-lPWdl@K7=XNvFr=g;2jU1LQ%m#2Bn#7IGZW)PSn-d;5oTEQCmN@u8YY9TO*S?# zwlG1f+)>?tH6%cpE7{n@(9*;VG$NH`XaLmL(^mf&{=(k#s;YdmS|l()Lds$l$u-ynwm~FNiwiBOf&)=|7-y5*I`s( zr6~n^dhnc%++ctdDUfC~S_NjDVrXcPnq-ig2s(A#05SS+3R&=OT$EZ|nwuJ5npd6) zKHbMGG0n)p)G#T{($qZ30$V4><}C6kCSICdS5=2FV6S7M8HB5Xg&-KyK30L$et(zR*TQ5$k}AlR($5 zni?jjSs0svW*a~&jgVH{kzqb`;17LZ(a^{+)ifp9%rGt4I5iczs{=XVkzy6p4(JiL zMh0dU=AgZp$)Ld{Xww;akOg8iJZ7+j4{VUdu{bC-4K(`&ZiIpl+=WC2@@bXsAu6ET z%pnH@B&ArU85^e=nOPd87#Kn?CP%&ikzC6m`at)Zf(Ft+7-6ZINs3u&Qj%d(l95SD z67*7eC^bGQKLy;QvIHG_ zY-XHfY?+#xYHH-lfTjkmkU*Gbo>&|Y@+4?$B&dcrGDI5Ux0??RR zd|qlr33z!1xa^3BE(uCCN-?qowUtdl=ZrzO1|i?P4YCh(6%?$@!HhBZJSl1-g=oZ{ zPEym7L5E3OS{hrJrkWZp}8eA`B)+?L*$`75 z^O94GOY$kbfj-G1IR$hXS5k6vVwyS9QbKU!$Q)EWIhCfF#uq>iBn6FEr5YHTo28ki zrdp&T1|w0F8=8RcGEW1Y8V5ZCE-fh;v}6l(nX;iFcv~2%5|lC>W-Zj5)QXbSykf}G z$CN~)G|;w{eG5>SgZfx-C0Na|grD3Fnv*a!w@frMu`o(bPPQ=aO12m6~QBZ<{6l<7(GgLrg5%lDHc#GNGEY-xq%mTED)X2yTc10l4OaXylK{gkf zU(t($RFg!@BxA!wLn9MYlT_$Yittt7DSCRuSOGN~S{4|mn5HHrTUr{M8Tn zi79BEZ)R~kOdcgSfvqz%iZ3onEGj7m--QKgXqj7B8d{i`CMBDI4kZH(<{&A+Vt@hY zBo?^#6btj@)Z|oCbCWcaWJ9B5R|bSMre?^olEo#dxuBJ~rY4pt=4nahsg{XmW{Du1 zAscfMp0$LR3!sc|XbI};C!1K9rI{N;$K2sfLWu87!QP5TDY*;{Qqv60lFST}Qca9O zcRi!2!E~k})EH#jO%e@LQxYwVlM{_nEmD#}1sC{~aJU*wW6V<^yI4SOH@7gbFi0^m zGfPWMHp1#xI9#e3Obq+wD~60(9|NuG%?8(bTk_1 zL@2Pvl*}SbeP*c@nZ+fb>FrcY@RlIs!p znv#-~XaQYogPM@QtM)TNqZY}QNhyiumTAT&DQ1?IAa{WZG>q1e3nZ$M%S=2SEYS9b z6jS3g14GNC6jRW~S&*R^%~(+RfN3HTMVYyo0jOL6jm(*tn8S)PtfeIm^U=yNbBk0% z!=yB`L<2KZ3lm6x2D43r!@bx$`H0dKRE>ZzqMvO5s@qMClakX+Q_~WS(3&ZRMv#^u z+N?XgUdP){1J%N?RRCzUnsYw%K49DzW}pl~SQsW5nWrWjS(qo887704|Hnu9L5CpV zb6yBsouAUQuLCpEc5PY-kp6>{4ga)}{GF{q9;F)>Orwn#NKOEb4jF))H& zT?mo|rF#oVV*!f`Fyk7ehg9pq+q2TlQj!f*EmIOxA%oY@yJnHvU!a?X3Lqn7q%8_C zwoEonOEfeFwSy8(3?L0?GmIRE&wQ{mQX$u8LM+E}nxT-F5`ECFken=I4R7kELYh z2V@qc7K67FLYHQtEx|d5VRJX=18{fswIkY9jRNU2K+^ z6_+HIl!EGEOS6=u6w4$_BV%*WB?^#PIh27>cxXZVhn$)4gqMM#5$F;#)1<@{i$u#5 zP^&A-&(H`mFba-nXt;rg@IV2FY%o{>xw#aUnxPQ_%Az2QoJ~zkQZ3EQElrG#OwvqJ zp-Z$3jgX5fJl@9TIvfXV!#xURfW|t(*$vHs7)Nd!S{Nr=nwlq?nVBRRSwdIfA)o66 zHBe8_ADjY^Ll9roB$^qgB%6TB>NLw_L&VX1u!Lm_Iw>>Xv>>wpytmKL#KOoV&D0<@ z)y%{ceA*FM20Z~obeU)7Wuz8?Z)r0!1YPxEVPI%rWSp7`-V+Fs!>-dLuM~Xqj-`>A zrGZ6?af-1)GH9zMIHwsJA-92GaRYWIvac~>2Bipv$>JIb3C#>6VMIlhUOVYMn;APrb$UD21zMN zsb-M1#n2@`s9r$Wi5$wvX$#tV1}$8Nj5r&n7+WM6nx~nWn1T*8N30cq`NR}lC!1tv zf@2%BVLv(1)HDS&4wGt%nAAsAZUjC)BPGqmBGn?v+}Oav%+dlH_Q<}4m~Bx~R2g5M z2u_5S$)@JUDQ0O&CaDHy#z?35qndAM22u~{U&4!1)5JvMG)waola$01gA~wGb96=M zDcK~o0&Hulg{7f^k!hkqnjxr*Yy|Gzz>lwnTb-B$IT*z_+0@uF**GmR*%I8Xc4Yv| zpqmZ}1kf@dBU6hcL!+cb3nSNGC}LDOwvHv+t4`G z($Lb#(#XJ-0b~P038u%uHkbyMrk19fr{)!cXwXW|)KoL0q+~PD^{bXCpm}qs9DV~* zQeg(97$uq-SeT}!rWqNeC4rXmLghdPAUCW*(W9paiXYH9Z^>nlrZbMB2P4ZGBwM5y zo0u7-Sz20}8-OAZG;)cwsez#J;ABRq*=}NCY?=((B5iJ-nqmmur(}lIG)A^2q^L3| zl~j*df_hh=WBJUIl0i#-!Bq$H>_0I!5ak#16f-lUR8!MrgA_AkW9aBT$`u8~SOqCa z;oV!nt_7tQm*$iZ=_Lch zWMj)TOY_833$rB4B+y81lpkh*CFkd*lqD8{+S_0Tk)|i7n3x+Unxz>UCa0L0fXX1y zhG`?n!~l9~EiFl{(9`n;Q6MRNN9MV}A_h983_AKQH6JIaw;?`Q$QzLCL3CU4pufZMd~7faCnp}n zHL{3TDK!V}95PQ*NzTbHE-gw`DK!Tj>|&mx0y(mIbBmNT69ZEN6VMe#W}wm9oE*p)1E_pQ?u3DonVufVHF|m=&kz}RCdq~= zCT4~PpaIj;`}^tzrw`CIN31G%rMc=B+bYIX^pW7 zXfK1gXI^nhVqS@nX>npnX;ETHW?p)H3Yd|f7hjy22fj_+$lS!-ASu->IoZI#APuxM z9;a$hn1a&PIBK_d?{bg)jH znp>Egn5I}5C0Q7ySR^GQ70Mvf3=M)SK{HRFx(g}3%o9^hlZ_0Lj6e$$4WR3Zk?&~- z>DALi=*4slI8{>^RThbc#>r-;21%gvH4M!m3rxs&2Dwp`WRYZ^l$x5HYGIUWnFdO| zuqp}^42GtNU;x+s7O=UPJRmT4_mXQL3I^ zaB7Kv9=H(0Xdi>q1E^TTEaj5TO^hr|EG$yYlFTfUp=0Nu;=~MME@UfLNn&w!d~$wX zNooaX$49D>sZpXqYFd)Hi9xC*v|$a>WN3gG+%hywEQts88$sJZO`yv?EK?GVK)Xef z4b03ElcAGW$b0$``t|fcE9Ody5|dN)^dRS1>*+yfwVV=@v%xaRv4uAnQ;iG_OcK)! z(~`|CP0W#QKty)3CEP|s1Bi|BX}Kl%D@y#bpx%?D|5F2!yYUEaKyfN{dV4lTy<&^Wuv$@{3Bq>k`3X37UvB zFf}o=v@kU@H!?9Yvj9zV;n83NZ8hi`=wuoj!B;`!@RuoM`~f@_4qd~OnrdNgn3!mi zYHpO0VhY-Sfu;sj2%~!kbkC(pQEF~}St@9z7Ie5YsFQ4(oS1BAWMO7$X<%Uv+K+{* z1cymRXePxY-;|haWSM3Px*H61M_L+alpEDdgko%_o92R=i^fHXpi5;fQ_Rgkt4|V> zQ%#c1pliR)A+v_yQU*EQLlzptEpe+44}!*&=92yPz)aqM)eMwJs6%xnK^;ZhvK+(4>W~_dPN#&>@v+V zEhQxlv|Jcz)D1aJnnBuXCT=;2>Co1*Nm5E`VzRk`g{5(tsVTIvjl8`IIeH-4_4ES3 zO+HAa;S-`lRHp#CeAU?4z|hRX#Kh1z$;>h}%@}-mi;V_&cLAvFYGnmZJXTiVE{A(* zVo{2XhNhj40+I@7zsJf7)W8L`Awbthg8NsR;H*eP=bM?Dfvz4+vP=S<L`?fWwe3I}x^p$XK;G}YY9)HKb|)Y8HTdAJuvXGsQViHc<| zsBAAuOa|R!U}|iTnwn~4o|0q=y2uW@uMG_$I^)3yOB;lMjq%J&%Qpe7C?Z(HrI?td zCL0-;npzqprlx@o+O4jJRt>eakc~7D^R28PM(aRjbq&C=jvk08&Vh!0l1Z|;Ws-Sn zQev`6D(p%#42?z*dmv{~S%MN)X0mr`B{UF|5)DjK3=ES%`v5@qm4j0N^e|V9atPTZ zJw5MKESFs1X;zt8n3$UwnHm{_jv7fu+AV+_Uxp~oH8LtnO$Tl2Es9Sm$uEjeOU%gu zwMRhfD-2T;&5cuyQw>4K6PRF6dc>J#SyY+_HXk&?on~nO+H9GemTZt>X#|=vgUe#e z9N-HwOu<9=AV-1jr7$)!GBq_#woFFqA0vAati{k2GU^_mUzS<~I$a#JraRHtAlcN+ z#4sr(G06yY_$!(kkOQEnNnpfHc}bduo?duK8t7VH%z^+CFR4YTX?l7tAPO{r=^mnD znF8WKuT)PlHnvDJO|h^@O-%*O8@e*UWkFV>CoL0@o#s$0jZ8t!*?7>jQ+z>wW?o5r zXvZ#^JRw~J< zrfDfD7N&-YX33xdX>jtUmg$f)5sFKS;!}&06Co3*7Kz4&W=SRn29`#NhN;kFJCWDj zAQ$0;y@S#9Nisiu||xc8TeQ<$cF3qv_$Z9hnb~;k#V9Cs9kK4 zW|nN?%7Cs26#O9bOu_98Bk<;0FcWG}Mt(l13{5mhOg2w5PBS$zGc~oeFmq)9tq@07 zh-R1}cn3JTB2d1uG)glE9q??PnrdopVd%<$RVA8nCeYdq)ZEQaMs=TwMUs(8l9`32 zacY`@CFp2dT#6aMt^fS|T!#4gqLdtnVNiBTD%i15UJ(ipEE8XqT9luf0#X3#LxB2| z@!$=eFsE9Ad7vHZ;2RF0p=WMllxk^VY;0g)1X|dX0tr2kB&I$~a8Q8cK>hb5v(yyO zi6bUyspcl2<~LjxQ>UR3%+FBW7M6y_hNek|DJDjyNy(r|PUye^!j-6c2v&26pp&aW z)wX$Rl5rB~U_%TOjZ2Cu%~SH@K?9V$$ilE$Fzrr^)6g*aH0gjQR{0h*tK%$Y63HZty z1L$qPFiBdw!Z%yp39MNfEe#f;>P04g`!K)YC%=I-d{~N6_#mX2}iKjx!7`($dTgQw&VfObio~ zQb7%CSQygDG0>niF*7huNwcspHcd$d-N6OEsT$z~P??DcR3ig$sRH&HWTgQ#3mPYz zgO(Z?nwc9JCMAPr@vtcbr4BR$jnUNQmzKms%>)&kCPo&ZodrfliO};UaKsM|Q$SP6 ze!WlEB9 zVxp00ss(hZCGxxw%veZw!4peaguMl1Y?PX2nwFAg3cCH(0McMELz;;PwWq;jU7+bz z=nx{Pmrz=anG&$tkY-|HY?^46mSSd^Vvq=(#03YWA#|y9W`16=Nk(FEhH-9UfuUhg zVqQvqF31k>Or43hYh;c?nL+%A5nX0D;GFDG7 zHxV>jm711Vnp5JDSe!xplwz7OXnDAqxuJQgQ8K9I0g6qe#eu1LWqNvHiA9hx4UA!A ztRZ5N3ffVe3|dELoNNxgssedCKiE*O{LDPiV*FxKj0P=XHv`RqC8e60CqmZ=BDXJ7 zAq%PtiZaU*OHx5|S0&&SPh?~!C#G1Wr5G5dC7BpnB!jL$0)+?CC>GRw=Zr+6ugJEv zG*3&lG%_Zfo?o9@r^Q@=mSDHggco-xZr=}!ZrW%-;m?oN- zLvpb>Qv4yC4O(>JR9TW*9G+Q{L83+GCMKz-CT3|Ssm2BdCeWh{kZ*-Swg@y}2U_}` zmrjabEDcT5OwCh_EDeoKOf8|;(jxC|ODhJI(}^iAnZ(YRgXdsUO-+naQVflZV9|+G zSb>c6FG$VvF92P8L2Q;cPBBQev;-ZJooE3%PaK>@kyopNj12&-Kr04qDJ9le1A|0! zOUpzvizGwC#8lAT3TTNBWGv`f!;s2?RALi!im6euk%^Issd17)8th(Zq&1u%GhH%^ zNGq+)5-n4cLB|6nnx&*!m_nCTBCU7^9UBMDD&QpGotZ~$iJh8cXl9gXWMXb$Vw{#@ z2-$Ln9JnB}J@Ya_yDu}Vh%f1kl8p@w%o2@_%#2M8K`Y(R0vBX#aB6BcD3=f!xG9#F zrYR{FiNQ*fKL;*ljYwpqt%@bUA3pvuTo1YLZEk zg{diYYzo5)@QD~j;DrN39LtF|uwkB(Y?Pd22Y?^41WR_wETX#SmYd}$x2wJxQI%YNvwAc!C z+@pE2feCc$7nPELp>c6ZJlLZ6yyDbk(2$84XmgXP1!zjxAT=3sMHiK91NkZ!=Bq@L zWHV!nBumgjdP~^xXVeKju&=-t!F-iwl$MlemS~uiWNDn3mI&E3L`7dELw%K$WRzxT zWSnN0VxDA}k_4ImrII~hUx6)x`O3&3%`nNx!Xhy((IO=Yde$hlY%?e>iBHPOE{1FY zw@fiLH#12|N;WV}N-=`YI8(_gOR!D($=To@uVJcLvVlRGQJO(gnz0!q{h1>53833& z!AHCy7etUDGOV?X1?T`_!z4q?RFf17vqVVYhTPVLmWSC-PWRjGc z3f%>SJTn1tEvOek%m!Vw=0S2AXgi^~MRKC0S&E@CG|^+*w@G;051P@bNhSuy<|&3| ziHXT+2IkPzfK*DrH${PrcFs>Bwxw>AWSnYgVw`N5oNQu}1iC35R1=}B2f?;Xp13Y~ zqG_s;g=wmRMQS4GL{MmeAaw&kOAKIZ@QK-wfEFq#mIldY$wr1ICMkx7=Flb|=JH#_ zwv7-XR_CMHVPcYOVw9Skl9G~WWN8VSM8mqs9J&ZXPY+`e1d%BoG#ZwaWNeyjkZfU* zoC=9yA81_@dQcR5vQVfzo7xpG5nL`_Y$de-^*$m&@5>iwN?nL^8s6Y-l zC<3+9K^VMU4|*(eT4Jh&v2kLOg+YpenF(n97icdZND}0KLj$BDg>q;31i+mF-6;>d zNEf=z2H9oisfmec$;qJe-P0@+kJdgTz!LQ|P6m z$Rn;8zQKC>Jf38306Lo0EX6p{&>U1LLq;FKBd$nmO~L8U5b0EOlO)KQt7*xmDdtAW zW@%|=NhwB#knD|GR29P)s)3Umk|9JEA<0HbMurwC28jk{$p)54M`dG#Ap9U*nDY#h z%#18S*CHpTB_u!^(!9(Hlf9x?3+MJMC8*CL48#m1A^ebEYeXBsTC!Vem;_VXjdORhn}0C z0y+W7)Dm=Kj=802im_>8Ds+n>x|KMFHgQ^sq#kOep;2;aaY=q|d|FOoI;b^ll$dH{ zVwh}UWMN`#mIS?V2KibjkXNw{QsT4}rWj_UX=ZUec_r);T#5lz;1$6Hj zXzyk!_+~<6HK53*sv|)6Jtt=*79reVZf;}@?j5FCrkGfOMp84249IZJC8W1inNdZxy^9}Nli#V*-fmAvc2PKxf zflnO-otjsaSRS7Sx;NP*Eh*K^z%td`$kf!_5Hy;VSR4UFf%eWPBu`QW|t%7C8!0 zt;@^lym zgVHY%=7Dc#1?}>VOBE2(#F=LZJ%<})B52!7vZZB`k!h-7 zT4Hjl8K~8XBo8tXts4Y87zJG0f?IhAzau9{Jk3xGQww8rqhtd!bAuEM12f2GShV%2 znASKJCue557K0Z|;<5;1*`t|3vVoy-VydBurKPzc^uASsJ_^mttbp7VhRY(f9(J0g zX>wAcfsv)5fw{2(@iw>JV zAp6=t%jJ=7R5M9QO|eWfG%-mwOG_~|hYZvabd8}oXgMfkfh?kxm}+R8VhS3AH83|Y zHATLX17Gl3AYHfyI#t}n+`u>ubgNAwX#Fav&O(~@LT^WcQ#K-uk<$UVXa^Nk=+}E0 zBwAV;8>gkD7$hf~7(y?8LF*u4rfVWCF*F7Ta9L_{Nq!M{Z8YdK4vS=Sb5qc*vS!c| z3Q><-fFF~F$gKpd!@Ql#*v!z}!pt-^#nQmo(g;=$A`Ja|&LfAcC$gzc|95OIBPXV0IM)G->_1$fuX5kl1Ykz zfpIEy!4Hk?wgkNG%qQ?6ntHfp@k9X zt}rtLQ&ThJG|(Z|P&rW5PmF`0l~1C1aBC{k&5kib_pcgu& zBw8k!8k?DxGbaBccyRBd0%bMTMSS(KSFjcgB!e{UoCl^JK$h14Gb$8&dpoGx#arKB((xOC!TDYoNQo}lwxL@W}IYX z0BM$BJ$n^23k8~>KwZy@KTHvNpuvhbw=6l)!Zal*&D1Q-C?(a>46>2|BPGE0JX#hc z7NzEu#Df<>7+R!(&m*z~odBK$YW~1wF>)#BqDs)XkV|TDGW5FWl6=sq?Vtf>&^>Av zW}tqpNwNvjqJDVRLe^~#Zf_)lipAtK(2-whX_l!*MyALo*JIUa0jbd;I+KzOQq9cM zOwv-5jV#R&o9eOZv`i^2$jMAjECG$vC8rn|m?x%Lnx-b1CR&1~J3xmD8KX?S!P_^W z@ePo-9ZR5hK_j1ShdlU-bd+ySW>PY&IR;)O6rYosoeEzul%7|rrl_$RgF)!ot81w2=V4pf*Y? zHUWjDS$;ukUOZ?aiG_h#l3Ah==op^FUT_yVjX zW20n4&;j1&DM=}YmaYsCDQvzrD@uiS#Er}h3{nhJ(m)}cYz8`}6C#C87kE6oAhif| z(}F2z!qPa=$k@y@)hH1<9EY9^Op9_MUP`t!OEEW3GEX)KEua9~0+PXM3COe1HDv~7 z$!5uE$!Ug0iD?$e$(F7RFiC7W&7rp!7@B8TrWk@Yj~N(Pq=GJQ#!~Eok{i^PWK)wA zV*_KuR147cx!5K-K#D=x5H$N>Vqun?YHDf`1uTQj^%!?17#o=;rluL2rI;n9rW)a}$s{?y0CYi`nPm#-Tv`i@G;?FKG^8V$ z;b{dl)?@}5G%?RhEe8!|g0_5FBqydMo290Jj**f2Q}w6hW>iO&Q?FT^;_(!#>r$T-o=JjuWW>GT?G4mUCYwUodY&O){+!Dczq zK)YPbK&`<_fUE$+ zYNPyO)8q_DHnRksEtzU+m}F>>m}~|rF~Ks}^cWfz7vyA?fbYfw%@dg>85o1EZcR=G z?G=KKk)o#S{9;s1#z~;f3819{&_dog%`nNp!qCLnG&RW}2{ev_qJpq9j53Sk;rD)m zMhVS9L&O%QM#f2rCdr^kz^FCyi(x0*rWhm{m>8#m2BRz!%~G+#2Q#8f%X$88zq{grXg*d zhbLNCi^|Xh+LVM&QNzw+H_l519V%mPlxCD-Y-*gAVxDM`hIG&z(FR$9l4W91db|Os z*fUQ`N-|9{N=`OOva|q209+O$@Cmvwu_zsM;DuR|fr$ZVSP*nDIdqjJO1dCuP;xnF z2stg$D9PB++`!!2)ZEP21TtAol0g;)MXB-72DM3&i9wQ~foY<7ss{Tkei264Uvv{B04h6ic3-;Gt5RQDT#^Z7UpTDCP{`y&>j>?z6F_; zS_C?(KiS+c*%UN&k!)^io??KdTSzDh3=NYL3qWU##i!+%U<)z%s?eGBpY0I#eYf zHyRouOv;ndLs0G8@%NRUB&}DE!WI6InmP0AUQR~ z($LZjIyYf~G|5Peqo51JFr9^URhW5_L5eYCAB#8HfPK6f!-wE-(bGfp!!hFkBWYwB8-e6(@r7zLPcyLqZ3hFLuV9j90GVbZ zqz!hA4)~flP(_w(ZfRhcW@MV2lA34%>e)i9g6xaNa6YO-KxI=rw0o19l453*WRRR} zYMx|f0J@1CR3m~U2^eAmx||+#zJM8MpwuEIDb2vh$O3vjGam0iPQp)1NwhEl9gAm{ zXku)Nt$;;!vN`CgL&&fTXgc03(J&>=!V%*Vr%oD>N7L~9q9^cRf9|c zZ7?x0u`n|=H8xE%H!v^(-QJw2FvFoOyC7BtsFP|h?;Ed-@TbHh|i zV+(Uj<77)iLr@DcSWv=lu(&>#|M91=6cu^OM2nrxbCVU(DhWMOG!WQldGuoygD4eHZ_j!7|2 z0xf7YO-?aMOG-8Z-IWGf`H#H?0jltkR_PJxCJPf&69XfI)U;HiB=aQDr5Ev0ekPDL zGMN_0hM-rzp2fbId8Nhvd8wdOV+iU{<$?s_^YcKXn~5fdrsgK5Mn;KAhDjFCWhthR z3F%BjGYU*LF*i&$G)gf{vouaNONH&%MT#Zx0YtEs^2iq9896fq-8cwp2bzJ8GllFe z2BlFfhhCT@7A0qxyFpIAFe}Lib*MpAVM?;4rG=@1kwKD$k+CIYY5|84p#GsLhTeii#PK#Te2u^HSndGvX7I zOEUBG7}T_KOG^|W>aDDDGLvA;_|Uv^kWy#J`MeDAp+U~^e*W=6u8zU}eh{vke~_!E zyI;I(gtKcvh^N0_yt8AdyGICA)-lAgNIx^7IQ1arE(tclP%SagBgl4dQtE zg}TOv`h|PKRJu8O`h*6#GQfNml$uzapBL|(pOOkHBMgm@!wJ%gK@BW|XE_;~Selxq zCYq%fn}H71gS0bDK#9pXzo6J6tuzlD=w@ztpd;QbLFYMv56uVFKqg5Rsb-0ZrUogA zX^=B=K{tR=>?F{gI+o_9phCeo)f9SIIB`xgu{1O@PfklUNHjMzwJ=VB9=Q+Nv;seA z%1}oGaxN&u$byn0Lu2p}UYU9E1)$prKqDq81}2sUhDK(l7Ri=L&><&0>dnl}O;S_S zOhC87rX?B}yE34dXoDOkR#ut01y)wB70IdK;}^gOD?tukgOCR zmNH7suqXn}Imee4fFeG{%-qu4GTG8N$-pGd*vypyCduFeG6^(6l3IkzFhfHmMJAw; zXizICISF+9t4Wf1N{YET=w@4xgiR@Eejgk_@W8iB&d)0@i7zNAGKTCDgZH3RoB~w9 zlHg(yde@(eXjOaYC=m{=w!CYc&1Tcm-m z7&nG=vQcv+B=oR)IMveBFwMx!z|6!9G-_oC8I!jug&rSGv^hx@sV0WWiH4w?NK!38 zV|$Q*vjB%0L1PS!K+5Bbb5fHGjVwU71e>L%f{yhuO*AqAEgOQk3#NdiI8C)QOfpL| zNlrFOGflKGOoA9?lWAy1lG{q*ZZk46Ni{XLFg7(wwlGcw6%1gs*hCW+9g6&u(o;HcpbDxl3lH%I1y zvyq9Rv4N#Ys(G44qPcl8^rjL_9j4&I12kM}mSS$1YHntdlx$#T0xgsvrGRNteno1E zp%LgLFW3pp(2itcN}7ogXq%UbX>wwUnW-y7UTQgk@&ME>$EaG2Ou#2?rDntzq!tzD z=OyN3mQ;d{vF zBMXD16zI|{@^UO_Qvvky!PMkbvowP=Q&R(Dvs4r4$ylg?4#~AfsTo)tWnqz&oSI^q znrdiiYLW=uZj4Ji=xX59qJpB-lKA9eXzK;kmNK$5H!%VQmm#RkO95XJjIIt8Oc2jv zsfJA;7tMgGHN?#|WhIG8IjMTZ8JW2#x7Qe08d-p%J}E8DC^ZS%GN+EwM#)CTW}ubX zW=YA$rl5WdsDTY`JYcO>4NVdg4J|AU4O5d0j7%)h3SrD@IMLk1+%Uz^z$nQeF~tb9 z_Z78T1viGRtiXxi%E~Ff0)M*3S?n04W`O3z5>w(+^OEyZQd1y@*@CxQ873PgS(saz zTco6db_J$DN-=bWu(LpO6DyNaM01wH)gs?S}VA(ts)-C|wF$*av z3@nUH%}tEVl1+@0j156ubqrOIBQH@iCxd=+Vo`d&KIAy%qDp;GgF8L5B%?G*FF8L~ z*Dy6X+04k&G9}3}HQB<*SRZt(T2fJdd7iF;o|&EzSba`r5~x8=p-~KQqZvT`zs%(L z)ZBuSN(M(CPj^38mw2bh5Z7Ra_{5yd^gM?6_*_FHi%bUhqWsbV21k%sY6|!WFHo+q zgc}9AQoy9RIMv7$tkTd3*=R#!6th9&4~8a*ppy(h>%z>9LCclYKuOig3Yv_35-amd zOBmb|b8<41a#9&Q^FSw0gP9PqOi+mjasnjx4Uv~~}uYkIY#)kQMsqs0f#l`U@ z8HsuEd8rj8@df$isYUVmY4IiHpds}%1LI_)M6)DgFvHB;m4SdB;;c3_$7Y#f66n}o z!<3{{!z9Br=unyoWUUrxm1+pM(F9$10}2-CtzGb2yr5?*C#IR2n3@?{rlcktCYgac zf}j)cK^oA)5uDi|0foIRNHjGuw=_sHFiJ~KH2@t9j+(cz#SPd@)c7|vM2Z_j^W?;W z#AHytgml6-Xv3a~iD{y#p;=O5GU$LY3{{|z#bd4|IE8@D8U_{L1|}AUiOELhpf&{f zFg~~}7Go^oc9rDECzU2=riJ&{@a&l6^T}8*7ocv^0)D2aj^noKmnWPvRnJ0l(w51p%nj0Iql3Z%R z&$84t&@R!9;hi6!8EJmO*+%i_x1r2HJvSuaM&wi+5BtThb|aSU+xw@iTwI*>{RHMQtc zL$g>gZ4nDfDUd9jmRn++SDFhOeW- zc}kk4rI8V2gNq4dxKE|jG)W~yrPMS9lrD8H zbSoyu<;B58Hsr%`MIE#;$UakF~F^c zrz%shqYRBfGVpLTG%3h0E{V^~104;TSOi+Znqrb{X<=w$VQB_Bg$>jUL{$Q@5!Cb~ zH#wzP7#SFunj{(;B%7pJm_SFtNUt4JlM+E!xtp4rC0iO=CPRivX`FOSO^uQg6U|I4 z%+oB5lAv?HkeE%&Ey163(hO1z%}fo_Qj#n{7r;Z@3(-qnsxY%KF*7hvGfy(KFilK_ zPKSdHBsQ{>j4cz*O-xJ;3@t$`ry+|f2p5p%MXAO4Ic2E?2RuRRZ;U{r2T7)&(I&_; z5U>jg4tS=S8z&nX85*S~nI#(=L+2BqheCl$PuPLV`MF7$r2(GFL-SMDW5YB< z3xiZsh?7jAsn01u1(5?m#Vbk~1D=Zs0NuQi3O;TH+DbD8wRBQU(~QiF%q%P{q1P%w zN`kbsOe?G8__F-W6mTH`J<|()^o&@mj4ezIQ;bZ(gQW)MiAYJvEFV0+X=n;LW)_~q zLA~+f{9MrRW{SCaN+M{PabilMc@iw&f!v8ta~@0fMSAv?g>(!ku*)WF=tFeTN%(j2m36t^ZU)|w`zCZ!~$ z87G2nSxo{RPaIsCo0*rE&rp!eP+XY{o}dp%28+0WYkFvf&ybRn8J`B02D6e9i&Md3 z#c;7=P{V-11$19+T0UxV4jbJzY zNZu?9=dZ@aRv1t zjlegq7$#YQA~*?jvq@@-Ddc!Nh+Z7I%OWu^CB7sh6|@x3Jk8L^($LJn(9+n_61-6j zCJDa6KnFBNmmHsxnwyxHo|9^2d`(hQj4VtI49${K(kxRG4P6=XvvFHZ zl+9Q}3#Bm+%N3v;23i}MXkn6SU}j_pnpib6gB1Tnn*ti7%mHshfZUS^8Ys3fOSLdc zN(GO~8G-7lv`l0rpavN6<{QB^LvC;cEw3|8HZ({!1dS+wrfQHCfXs%3J&yDVz74=U zBiS^?I62ka$k5o#FvSpbc2I6&fsI;psYR>~yx_L7Dyb|;Ew-`(5B~TUg=eOug3d%u(*P^hRM53kAVLE;Q4sBEvz*MlRPedKNyea+*k;DYNydpL$w*M zWSEj`FM2FEoni*-QP&5&3Gfr6m8q=;ZClxUcoXl4RBXy4o*$;=X|pM|r*kzW*_no|sF zt(brY&{GY~Obm?7lE7y(L**c)DeNvkLvX-gOCz9*u0h>Eui8Gl)ph$bBd*fxuuzzc~Y8T5_mlq zsuF}-k!N8{LH*-m=*c%AHn_D9%AtCC;ATE-)r6iNco+uM@~G_HLjW z-vYe2A{TPBnwf<`s)?DQrJNjx--KpSpppmQ%xjg8U_49pVELHBiHDg%X;C1i0GQcx5n=B20V=>>r(P&mLx z$iZQT(w6~`2I%R5ule-^rA?4}hsj-;>a=#0duSjZ3BL;QATVTyhl8udx3=B*y zAWdnoPe?Wtx=JE7)yy*4G}*$~+|ne`5Hx2G%B=KACWfFJqfqXnG)e^xGp8nkZUsrT z1YHjZatk~QA>{&;<{I+YD=6TN%`7cImuaM0nkHF7cP+ugi0F|tqyjB1(a_Aq+{Dz> z!aN03ScA{`19fg};BEsAFyk3SwqpRDUX@y03~D`thC#wJ6445BB;OeZLB>iziVQ*V zR0Nu*$}frs_lwO8Op;TKKxeC1S{fvS4wV44Q{gH=*&O2A+|*n%&|+lpc{WMOMu`@Q z7DT4;Emc=R?wa$&brbJGBgbv*8wd%vNSL? zv`jTNG%++X1us}8F8WL%CL!M=YmuC4l$c^{VQOZOWNHXKj~Huth?)}6jUw3Iut>8= zN;5aIFf~j{PBDd^cTdLfw6TGSiG`72vPr6Oidib?%0p125IRH@oi*-DHv#0*VAw~HpF8WMm&CD#-(kRU=Ez!gx%_J4FjKmDGNE*p0kS0uKRVrw?G^p84q_d0-O%2T~ zjML1`6O9thpsTyFISWg39#lW1f(mlO#6$}V(=@X*jIlttvyioe&Z7YD@&)aUPfRp2 zNHa4^PE9sQPBSwEcjCa3pt2X?5NJc!2+=eLP11ok#3mbArdgzznp=RDktc(0j72Fb zvFS84OiW3MPlha=1}(ozG)po}Nj5W2O-nL20K4Pt>2euJ2=_cpo=cVfD zIfE!r0dEQx038%#Xl9mVVU%oPX%6Z?K#F5TfFrTsmY9NPE-b)gd>Uxv(84q|8FVOx zp@{|P^i5+|2ACu`m>|=HNbZG17E)dVb#XBr4XKkb3hC4&W0N$a6wuugMuwK4VGGb! z5J)4DTuabOGE0*b(7F|4lf)!5lN8JmdbkG+QEGe($o8M)Bv5z5Bqh}($=J-q$ROD` z5wy4*CJ71?q;SPy7U(c?@aTLz*f>jbV>1H-^EA-(ZE_;04N?plypK;#BF;d|0`Sm2 z#5nULQ&Zz)BeO)yL^ESE&|W{dEODkGwX}>tD}+*wjVuyVQj83sH9B@k36VRe7P$Ee(HAu8HGfYiNO-VF_Ue!sLZZW9413Dte z!T@wmhH(<;SkqKf$VM!%^C6v4BS^M_cSg}V*ichUj1tW)jZ7>Jk}MKaQ=u2r(#0F_ z-hHa6k%6V5sf9&aVzRLjbXF9}BQ)t%CtD^Nnx&+rnI|VD8K;2iUQkJdw5-4p$0`-% z!Ha;zBG5IBu7#i;J!Ieq)N@ZQECt^|Y-W&}m}Fp_Y-na|YG!Ey85&0Nm<6PCg~t%2 zdO_P&3A$wiG&0}>aSv!{q}0ej1!dF#>Q?s<703q6B$G5#BU5A0`AH^;si0$Y;BDdL z+ycm`7W679!j=cQsW^cWJ~*?(tqpR6-1iN!(7+O8n^BUbiJ<{#qXg2X3p`oLJUz7p zdgz&{k!50vWr~rxxh1F(fHZYx3^D*Mpo&YtbKsaA{}fZBBuhg>Ba6hORL~|=BzKTH z?}!*HF)=hxGqyBIOENVzGqVIW#o?hzs-eb^9VNy|$!1387KWgcl`Tw=C;Lo5Yl%#Y zK!a$8(B1TCF$7Y8cd-$2>VTvu@K7b#Or-HScyfgmXeNdx=H{s;X-3B8=4q)$Nb~!6 zq5!;_-_Sh61T=eMnF>1H$}Bm})W{XQm(K=te*m~^LgS?|NruKosb-)G)-=h|6s7YA zIzA_TQ*~+3MKRzWhJ+lPV zMM+IgNi;T1OG!4cuuKKDreKor%91!UGmGJ7S{kG#StOYmrkNOmZj4R?kAi_MgqsOU zab_h&iJ2wE&=sf%SLPR`Lfhcr5mLNks?dT0Ue%b$FLyJ@+OA8~*6wu+7Nb)wNkS&$aOOZjNkf2EcS(H0q)rFfmU_0+;r11t8CZ z;}P8Ofu=PJP#XtyqN)XG!7!KszB?{I2ecj6zzEd8H%~S;F#}!CmjW8GsEkj`&q;w6 z6U8M(R#wg#iABXa8k&gZ1<>>kGTX|^CqKUcbZl^{jfSR3_f3|I2Ck826%Izd6IFmd8(O3T2i8kc?$B zVsesEk}CtU0#JUigjBbXBvFh~1NfHalw^X+H`hW~t>Y7-;tZ;KNU1z9)3|A}p&{rt z7-Mq_O9Rl3X_Q=mI9CIlv!KW8xKU&!X#T^%(!k8z+$=fO5PEqNq0of&IW3cnjFXcS z(@ZQZ%`FW;wK!;NIhr#qAsg7B;h2+|oJ!($p45uOk^Yof-|d9Vf7Ly z%#CxvOK=ktP0SKg(~J_$EKE|t1rlf`1~T*ywH;S121CmkP(~*z8sQq;LsVehcgrNh zRD(pLRKsKo&|qsKX#0Of30i2tgVr3{aIt_HWoQ8IBj957La%(}S(f#IzaP+zR0;Jvq_b z$k5y@(abo>#0c*%*T1_sH=X+~-0DMp6SX-y)`0JU*I6Nx4krlx5IDdv{uDaPPKWXJUG|#X|N;WkyFtIdCGc>R;2GuVxNl=alCn#*ILr_k?GEOx! z1RqFanFd;FZ3x=Ios$DT8_OmwPlIp(>44RMi$JiA5Z4kIgflb%UB8;2S5lM@>Tg&Y zB$^ncni(XUT9~C8gHGi|NW;B?E7}cBO2OlV@z7Pwpk|DvWwHsVNo#6gkdy>Ern8_l zuM#vu%HWxjng?D|hTdHOaE6C^sI3RJl&CnFnMakyoiI$e2 z%S}KR78zT(GN7qJxC3lBN}B;v&L^fM7L=qG6@&J_7i6ZUfa)g15>AxvIz$6x@KR3? z!~^wUA*~J&3zV#3m9}A$8R%RnQxh}OG&A!=$S4!^CM%G~_4L5?BzWQ#vmyYQO>7mE zW?*cZ0=jqG(l9M433}cjBsqeNv4DBd&^#bDH5+um4BU$*1*xfo`;3O>X30h=rfFu$ zhUOLqptb)fea8HvR4XfRlL71*JJ_ZJkS{UHl~N0&QUzzKwg62Zffh676{i-JfQKi* z;| z8MIapW}Xh%!FCKdOCwM@inCn7Q|1wDd!<<XIeSsI#vX6jM0MoxZlX;G>! z$z>pPc^-O$rp!Q34_r)R78qo;9E}Z<&61K$4N{HGlatawr;$NY4>Z{lU1Wo!5|#qd zw+dscqD&Ia6BCm_$5y5or-DvSL9N9|upgcmAUk%zE&-i#f%8-!LzCjflG38YlFYpH z_~KH~qM)>-#FP~C)HD-IOUpDf(7mmwN1MSK$Iq8URoQ$y2K17q{V z#AE{lOXL}+Mdm4%si2E-KufR< zQbBi}z_PY6*w)NEh^+?3CMITS7N8^REz(ezu7eB%+nNbl{$*;Cnq--53_8QW$THOw z-FdlSMJB09hQ_9e#)ig;7DnLP2SHt1$XW?d`3kLh@Kv-&;*q*RH!CFcc)q= znIyV zNuq%zc)KS&_DR`cg_yH8HL^&xNJ&XGHApr{G6#+LK#tLbmcLrbpc6P1Ky7jOnI9mI z9fKN(LftD45`mum0b#=21}VLXnEo~}2OSv)I-kzPW4~VhFmARm-hNYl3 z1J7a=ftxF!Ca#5fVycNra%!>>XoV9bY2(&roC>}QFUi6z$;i~w$izI^+$hZ$vZxZA zuW=P!(7M>d)Z8f1&>VEimyreRHWU<%*lS(TvQE&nhM}oZs#%JWX>ytw=(u-KHv=pM zt98LffpR%y`3|(}j^4e>EdWhL!B!<9_ozxOKn+hL@B!^6iD{Mwsb#Q4MCUlB%4^InVKbt!U=@><6(qc% zlHiFML=>U4{y_sq7(?*jt_x-!09it)ABN&?OCz&1OAE6U zub~0tuz5)AgB=X%NrIvrN0vl&4slTqs!!uVD}F%*r-6xufk{$IvZZ-ik{M_nQYM}u z0cfdf3RXj~DQs$Hl45CYX<=!RoR)&J)P>k+CvtGi*dQ&{)WFi%6qJ0;%q?9RlJg5H z<3WiRl!f6%2-sRg-_00B6+!Q$CL5=KE~-sSF-SB?0Zl3)x9mavFwn#SbZ`R{FF5l$ zqGSNaJ2)rcs&TM1md#BpER$2s3{5NyjLlO(yv8Kg2PM!V@c9Qx$w{e6iK!_TrluAa$)MIUR1OrexJ@uL$u9sM zIfmp0P>0blF~!`#2(%*;bk!!R5&{Mqr(_l<<|bvPmx9K2Kr<&sriq3I#s=oescFWk zi6*WLC@KgTXI7k@SpeH+9}l@w&M+;-(!em)+}z06!qgNrR)?+#zoF1d6&&KpNv38N z$p*3#1fUTHLt|r0bE8B{Q!`VGBv88(A&tWXXfb1Gn2}l$pO=}GlmniBFiuN0 zPBlxmurNw9H3P4iM3M&?MQTQ}G&W7POfxi1OioTSGd4$F1q(U`7E%|(TV17)i-rwN zVW*HH`iqvP#s=mVX_hHT=BdVp&;$L!t^xNvaTsI>UZ9bh0_}8~85pG)8-gx1H8L_a z1YI`(9T`RK^jle#89*0t73p9cn*);b%qvUG0i6Q|zEM6tCo?S-bY)I_N@_t#215Yo z=m*F_{$Lm4U0#-091l6Vz$D4S4797*(kvy-$Q;yQhe%~6Bq-~CI5D`cU?l#bA z!|xZh5F%K)b6; zb4qLon4_bBQvuWZxrJGxp&{s)ealo(Jp!MNBIFQk(F_fJ%wbS0 zRRdD`0%Zg64X*?%esG#kOg1#KNHnp4-Rey+?;!g)1=Qh7Gqx}`Ff}ta2Mw!H$H!(S z@W_N-zkoMO5$|QA6!TFOp56?Sy#neoye)F3^_lWD^rhO9P7(6JyZS2$~v1Jwl{MQ$XWyX`no8 znF5-sM4DTrBsF78>CoH=U7e?keW4+8DT*Ed1e3Z^s%4^is;QZUg{h%Mk`dChE3ui| z%m8%uPMWcyiD{~dfms@rf}<2EsY3>K@kP7=IKqj`-DZXsrp5+_78Z$VhH0kAdpYoV z*)+2_9(O4Y-E(02AvE89h8kH<6x75)gDRt`8n{ylwi&R`4F^Y zCNbH{zPh`81wNKH00OEgKdFfz0>OEb5iQb-^db0$>va++D1kvZs=aSNj)6C=<> zFToZ#sB9pv3Nm{aHl4+v3p@E?RXyk}ap==5cWs*`vVwzD(Vrr71S)!?dxh3daFRHi|Ifoh8 zLF-EBQHP*07*P8UUxYyqNJP>>u%=5&N=!;IF}5@@0k2RqM(otV*TO)Kx75^B6H|i} z^VGy-BSS;b73kCn3&g?^Ls(FNMr{a%0knAqQcBSCX(^y{W)jUTEz%6kjF1nXBi8dq zM&_oeMh51_$)MY|L6h;+@jOzClITK!C@&kCCMKGjr&uIgrkWe1fiA%zp=Fa~YGP`T zoN8vCXp(Gd2|DH%yv&D!mJL#uiD0|Nkd&x3H#SN%Hcm9KOf)ezG)lGvr)xZU#}c$+ z0kp@n2y~orvRRU0a*|PUl92&ulLhF$w0O{cDyc>A6?%{wkfc(>G|kY!(!kKfDAB+m z(bU8O$yfrZ8sR#EY1%9m)ICl~OfgSPF*C7DbY(!E34nFaAg(7iJSM91#wMWJ^;sLxa>rbBmND@a^c> zLIL7tJfUES*`+Z|G6J1YWNK!SYH45qx>OjJfp9geEkKS)HA^uzGBZlCfF9pSnx*EJ zmL`eGDWK(BhN)?wyJCoIAQ4q0Bqyeto0}RN8l|Krnx}$>;^97mAJPHcg9;yS2DOUy z^pNM={fkn-6Oyon)6fxVqa08_&>$(z*w7-)!U8lAk!%Q=odKs6@U+*!oHLPXXl9UN zYHVO&l4@#XmSzUss8VVM$sAzk;fhm3LjwZ~Gt0CT%QREdWHTfW5nFQ@TNs7RIR-CW)5G$rfftiQp>%;mM@9qzK2%o@p`Y>>|(% zd78O}QJM+(kk{0dP0bCH6H}6tEQ~-~@~M&l2=^)= z6)j0_H8wIavq&^gO)|AGFirtYv_ZykQY!P}!3)d@)L({>^Z(-E>IsIgrIC@bnMI;W zN@}v9c?#&TVTg_3m0*NyGelBL&?e9&TxN+WiI!=W7Re?CWW*%s20YVLGt;ybLo;&| z(4j9#_Q#iI;@rgm8M=t*QZ?RAi9? zI`k2Gj|agS9;|5$GO>s4D1Cf662s@9S=l5r3u7b86a(WF3yWk>Y9qdUFf_DC1sxBY zn3j~3VhI}Jg_aMnd#=Ih9?x0?*rrYk(%##g` zO^nPDaFzPGVX?+bS+><{}Iff$;L)T zNd`uiNk%3~rYX={EWwF}uxp51T4b4KWS(ScVQOe-VrgLsz4{E@#n9t0;0_1nuQY=s z(D}E9X%+@1smbUmlBmEovPeoxGD}QJN=~*kFflg5Jp2M}*&%XI563zr&@L<32^h5B z`h=Wxj1tX^EiKawK+E9_4GjtVrqqHaM|$KzR_Ys>ry3X<8Kzhoni+zslf=AA=t@CQ zE*cce2#piLZnZE;N=pS@>1UCgoRXB31R83tg!c&{Ee%2`l3-RcvNScfv@lIgO0!5Y zGfG1`F@aE4%Bv(YD;b!Yn5HEq8k!rJ8yQ)oKrVJb1fr(*~MVcAt>S=iE61=7t zZD}m_X-o97gJ4XhS{NjoCYpm6Em?rK%3$xOB9}&%=BbuuW{HNDMuw@z7NA*gSZ$9# zc9HY1rFoi#nYnqAd6J1iszoYfB!Rv~h(U5P==AO+BeO&c(?rnN7;)Z!W~5Ye!<58C zi=?D9gCvt=OGpBw>IO`xMMj2(24+S^mMO+2$%#qOBOlSTs8N{#JcjSjYg?Cfc2V3uZ{l9*y( zk!oOSX@=B|$G%y_AU82PH6FbB8FcV%s!^(urCE}txq+ogB4~*oLK?Qk02~fj4Fes( z4l~Kp+{Dm4$=oc-+%(PH)G`Hp06SO`#T-zx)f99XL^1R(C(s4}vlIhU@MTK|CW#j2 zkfZ6*4PEKYvXziqNVycB@vZaApN=j;?WuhhcI4sCH(%}0tKz%H*&AYY*yi5@rv6TGpRnVB(YDT--g zVw$N1(p(q*umUwSiA?axX-Ub6rfI2$NhSuMgZ;rnqu}VkRrrH$cY)qmuXb6o2$VsA* zy~Lo-5Ehfr&IC_1NH(xEwlJ}@Ofok#NACWE&R;b&1|6s$pP3h5kON(FY-nhZY-Da| zYLS#?YycX_LQw&2r{xiQm?7HoTZoszIUoIAD#{P)BABofK_QT0nUZ2|nQD-hiZrMI zDtVw~0KL!uBT0C3S!r zV2HjphzIJVV(+e|7$<{nIyFtTG*7cM0nOxqw&_6P5 z7$<_3qJmBahxiNK5>OT*VXrl)-bhU`O*T%nFg7&=9gqR3QqhY>qcQ{NdUwdS8qofX zGy?=B+Epj6cf*+Rc}~S6R-z163mB_)AUp8+g3aA_eFCD5pc4`qV}R6tAZKs_xZgG94L3!Hfg z9xuoP)Q096md43Oi3Z7OMi$9NNvWVCkg#-{;0Gp5mRu4n1JHL&=f7AL8tauCV_?#%o8mwOj46T z2_!zs4`PgQS!%MOQCMm+s1X6)@(Riopi9%tGfY#A4NcOF%#2J-%#%_KAteCxd>XJD z^z_P7llAnFbz>%3@L+vPYEfz$sKs5Bng$voHw6oT?sPIqGc-4|Of&{v>z8N=S*iui zdBod+HRlv{6Z&KpmzbyKl@w)y_wJdb zq!}irnHndjCK@IsS|E*wgSr4@T)|+PoRVgom}HimoMLHW4!tf8+|3dbsh%FxaiIG^FdT;~gB0Z#VBUCUnP#4n zoMM?|nQEM5UiLt3+k~wq~9byD#$%d>C zbSI&SaiV#0TC%Ar^ztA>14y+DjYYg=8?u_7E}O&u%TgbL27b*Dp)J1Z)BR5W|nMfWNMUXoMd2{3~mYI@{FN5L|;6(l@7KB zRMJ}*7$+H9nwX>*n^_tm4_o3IVlmCnj?V|J4@@;k1FbDgH8nIeG&KcXazbR(S|sM? z7nNjIrKZH^rIv#a^99{mlVp&RY-y2bYH665VwntyTu5dIAEpF5!pXqG9CWXXS)!%6 zd2$+P+ZsFx!VgmdAK_$?YGjdOkZNF=YG#p|49$0N1EJ?U8JQVcB$=6+B^iL$Zi7}> zz&(MP9t;g2TWaGW)txzLKE@yw)Co>APXw*iM)ZG?qZ?k_z)l!P>2YIJcfO@1pvbla z*LcwFwFaii;B`lq#>S}@iJ$`~@i^1GIJG1`2Yd`N=#X^7G&5t9LkURiMyJPJuH1fsyzZ%m0&kCDb+N|FfrLY6||f+5p-uC%uw(d4n>)H#hJ;)hL+&- zGV=2C^2<_-auN#)GV{_wgIXD#%=Ok0Gv8d&SnK9_x0njNG z#z_{??Wd5U2Y>x&2+kRaC7^;Gv@XQl!YJ7!DakMmbR`P3k$`57DRPw!N({#3nI#$V z$%zGt$(bdf$>PLROA`z8 zKN%NxB?_pNM%M*8QyLU1prY8&)WjmuD9y;oEY&;}+WCdv0F33t}r%DGfGXeFflPRHi4}}fgVx~_fk<&emUq2)N)W&fw&e9fB6Ek1>T$n zU94knY?hLmn3iT{X_n&304{cHic5;XBjMmwg1s$}Xl|L7mTY2Xl4fXNV4MazTB9hx z06t0#>yIVngIozZ>!%!SD%e;^3}Y!_F{0Mk(8Me;(agfa*gVN3Db>i8p)3`2KzCtj zDg)?Bt$0w{1od(tE=|iVv2-j-FU?KOD=9X#NG&eO%uOswO+ijA<|#=@=7t8T1|}w! z7KVnYt_+yU5GIm(sT|@wHzQ-iH1kw50}Dgrv?N2&dS&Q&ZfMJ=NVNf48k;5>r5GnA zrWzV0rc=YLcO$fvIIuY6@u0jw=Ib{1;&&jXN)< zMrr10mX@g|$tgw_#wo_omB^@p1$HZR3?BbND5GT1l2Wr|L(l?sLyKg*YjePspsfag z9SR27xdq>E3t2dzpa?k+3>?b%9c2PK;Rv*`)Y8-}Db>`>+{Db#EHN?7l>sCHohkuW zgwVsUusQ~uZ7d;EN07P}6gkL;kNGARfQkW66=xM3=k!B#fS0NkLr#grGPnF5YazY|Il#&a)6KAr6$a3t1Rcl?K5|I`tPi}28O#Fv#uT#J z4=jM$%RzN)Vsdh6uAW|SX)dVvGA=I71@#9k%`Gev%@R|LObnAuK&NvQm*zqP6r>$_ zjgW6*0aOOl*$QxX6CJ5g7o-`dCYvN#8dz8u8l|LwszHif00}y90D%)1QCS%I>?ote z#1wOr_%|n z)q%Ja?m9%|foe`NhE5EUK})49Q%o$3OcRqq*J1kQ=lPZ9T@;Qj<+Bk_?SgO;Rijpv@+@ zDkJ0K^2CC8)b?zAejaG$Mha-s+r-!abm3Q;5p=}|L}zYlt_esZsDuD5AxySNO97qM z4_=@N^M!G4YOZ-vDttmaIoZ<8DAg!6$;imW0_9*}kZ~o6;N>DlX%?W1EK&^vtTdB|nhGwa0CI)G#DW;}ot_8$P|3~Grimbzi7?E+}P42CB-}~H8I&d5p=3NUHkoTcYyW|nEGfT2aH8n7@NCh?f;YAvb%askx(?H|msU}H@$wn5b z80-58BzSOE1&tyYfG%zVl|;s-riP$_x03w$oc!|CqU6NlR8Xph6fK}tGB`HE85$vL zF|-6V6iN#UAST2YXQt;Sf>M~7Ws+%%nL(nVv4y3nu^IU2n0PdGAp5{U4YCee%Tx7k zy);mvX`W(YVwRMgm;_qC01szUJ8Xz9p_!SnrJ-?JvV~cynYkhI=}n+o(Gt8U12np5 z3|W8%FPK%F0#v|~;C3>2+6~$ZOfxh#H!(J}G&e9dG)PNbk)@HPv6(68 zXfovWfS?#MG6rw50bOYmUyz@fR}x>8T9#T=464f0EEAJb(@axAS2-G*gIYT{)M9DC z!|o=rL^+rjw9*N@LKGZaD8nJ3egR^E6XDH7?jb5D`-6;A5)G0}%nZ_!j8ly*pj+y} z2@#x6ped2m1FVhB4NX!L6AcYblg!N0K=U@BRSQL+Ml{URBwwQnKAs2B8YIzqrbVFZ z`#>XfNh#)*Mkz@qpqr1uL&#tmOlP4se=Q*?7}QgQbTyH?s#MrA1$CWKVro)SvSpg7 ziD_z5B51)YMpXcbam0SD;OxwT@QlnH@Ypn{w@ihlaAtw@A-GE(#6L4u%WcA|ly zp=oMLim{=og=LCqD(KRz;#BO)p-v;p(TLOqX*!|zH4F=iQbAWyfzELUO*UB?B%4{7 zB_~>#fOZ3dmxRIPVa*zFUIG;vhUValX`wf3fm_3%7Jag%Ws<2yT8d>NsLV5Wg= zW=ZJxzd{RRsx}KuO)Zj*Q%x;EOD#-N(~ufd$o|AXG-;laYHny~X=Y?-WRR8usB+&k3+=U2+%LZ}gG&MA}OioO+NHH@_PD?`GA&)<~ zf|Cf5nb0&PB_+)SR12FJq#7n74>N$uaiaPH_$n}hrL3`WT4HLFg+ZEyVXCPmXkZ0- zhz`^QfnIh8scPXR6Rsr=SWfg$O*A)6HnRjBAZq}+f)X*2PJ$sOi7CdZ7Ri>02BwJy zMkte=kouEAPt2?+wE%K2j%A{`xrvF1si9e#aiU?Ou`2^a3Z)Xo&^3pk%bHQwyFe%Q%^-_X zK_EXC5$B-z9~H8IsF#UKgNbptz-sMG+?z@P@YX_9%0 zsj-DoN}{omnQ;0`U}6Ls+dyh~BhU1JfL5iWFg^4lf+%S*?v=RYnMeZAb z2Ln@}lIZS7nwfeSVcSJ?wS9XQzxbb^wZk%@s(ig^nBbl_4$Gf*6%)$}L^;=i^l z$->AyIVr`+z&Itv!~nEg2t3A`Vgqv=wAE0CzKGcnyzU|%v=Fl(v8V*JWg^MU$jBlw zIXTJHAT`y@610IZuOuI#1Xk8Uj3lt?1=PSRN-c)2LNre?F*GqYPBloiG&Hm@Fon!b zL*;EsEp))et(6s&a?i<6O3VSbG9Z>=4*MG#qS|0!VUU(+oNR7rlm@!k!xG&FuwmxN zeRRk)H|RK%;?kTFP}&bsaaM5!O}ApEfABi5lA=oR&Cwtwh6bRKYtXF;khOuyhL#qV zmPu*J2Bya5pgnX5Y0%PisK=nSLuP(nv6WRwQDqP$gdjHygEA__yZFP_9LeYLsTGOf z=_gBzIpPI5GI*{2BeZKlT$$3-jbkOgCHhh z#yax$_hd86B=b}=6T_r50}G=x$O2f3Lj-MN$uQNz%pl3!BF)Iyz%<1ivLF=dI@|}? z85)9CrNu)gDM6_(8FY27Wm<}HVrojNDddnLh$(0@nUG=?y!Z~J9W;QGl9ZBYW{_lH zYG`a^3OeQ*Y&Kj8BHLpLE~GVoX{l*O#wMnQ$redQX{jcVi~?~3rba{K;*6rqyzKar z{P?s?(EPMPni(izn5Lzgn}fzKkP-+M?Pkbs1fA&vYOq?Ef~I6GQZ3CBQ_`Tj^f0m@ zt~Hs)DQ1R7siw(kX{H7iNzfgnVBMg64x1l0Pfa#WGEFp0G)zrSHUKqm;OlL*Cl3nvO(=-c93(LfmWXrS^P$34E z!BdDq`%K`9)zBDnSs|zvMJ~)-K&FEP!815Gs#VZVdObbRE;~?(rl*JCyO$;wfg8@m zYc~Pu1`WNMm>4CQSQr|lni*P}BPUi+^kZ(NNis7wF*Y$bN;Ee~Gf#zX^uV%}iwqyZ zoMe;?T0LrPY@C#sm~5B~IqU_V12COrY-D0-X>68iX>4L)U~Z13rvYv+L-wlU31m={ z8!-a}ZZLrNAb|R)iOD6wnN_Kv-4~z+JtzW@g$X2ZEJ&@f;meGP zT*aK0oSK?uV4j!+I)Kj95Mrwd>BmVJ8>bnWnp>u%CK)HE!3!HQ%rQ^1Gy`3ol9Xs+ zX_5-5t;leXiIIhQqG76;WtvG+T9PqzF%s$SNlZyjHcK=~HBUAHEmwmc4MDm&sisMZ zNy$k@X=WCdY2aH4lWBz==ai$qh?6jQSl(3TUkWLE}Iwni+qRs(Ivg&R>^QUo3O zhD{j2=aE4TF!&y$U{FN{Z^)5wvPKAa$vSxH8z|v|Fr@WIaHP)CFwrzEEh)v&BFWq^ z71S5T>S{>oNo7~V@&GyRHcl}xOEobtG&KYriVhtqhc532?F`5*Edgixa4?B7;SAnt z3f>q83MWGYa0@;jH0S`T8x1T?EI>VmG$W%#Q&2|`RKG*!vPm)*Wz(I7ahheSfk~>7 zxe;hi2GWuw$F)#XLF-D)Gm?$WjZISwOq0w^(+m@#RS2Xag^-388Hsr*IjQmBDi7Wl zGz0BXOENMsH8(U%OfxoiWx!MhY675dZm392f$!HqPMj#Ao06KASejD;8n*#6Kw%6T zPyh*n8k<={csPI2IRYrssibGEh>0=>lzlF-S~F ziBAMegA$QN3TStjsikppTB<4N_(6=`9i*8?t_v`&G)*)yH8V;zOEj}IO*RDG1`bNM zVDF;rGq;3q)-W^=OHFo3Ei463yMr4rmhk->CMlU|X&|4Z8dxNy85pFPrx{zCgO-DW zBtVG+lERGBa!bt8@{4l8m&2PG8d{ngCxX`7Stch!_hdp8!W+ltupUD^XiHRDPJTJ4 zJDh5mY?)|mU~FiTXqE~(^91gC(1ZnaT{^t@0ecG(i`Z60LL&#(*#b3iK`Z{vlhZ&W zqh@BN$%&~+M#y~(Je@7nMN;M&X$F=CDQ2LNJF_%%@FX&dbMeiul)@HQf!1akn;4~~ zTBao@86=sdr6#&El;k5;RUrj3Zl@t8AX1D#SDB?+n5SB#7#V;z|G?d32I(fk*0P#{ zM}ELl`8oN;#h~q4mdPe*#wG@q2A1YV$rhlcLTG9bjzKdFGRjm68l44&Z=z+YfqAlF zl7VTmu_b7B9cGT98BQZ0JyPU>J_BED33d1mGN6RhK%>mO;?$xN@PY}@I40qtjW|3rI0%_5jK`MMSFF@9UW15V!CQ3?5GcvL;F)>du zF)}rS9%)3G*{}i)l&?TL#L|+|EX|CKEi8;drwkY%FG~g`B zy$t}G`9a#^nPie=mYSB7W|3%MX<-SyLK;%tz*4^fq-6oGIUvn#T(-iJ5;USg)`2FP zV0VWQa+?V>QVc9oEiH@^Ei6ooK`lvW0|atx3DgWUn?Sapw4!ji5J$5C0NN-5g&r(cEKST(3==JljEszpjSXRg<0!EL^(bV{ z3$ygY8Y`wrDMm&HDJJHLhDHXame7Dhi4~-&Ch(*c@>CO%wwRch8ylM&C7C3fnwTdU zKu*XpgY;FQu7<@uQPmIH!C%G(NtTv|NtTvI7HMXN&`BET+BNLP6R3R9%m(#XUlIoZrODb1B3B-|e?Nt8JjFx8-W9OGnz z6mw%!vlL@fQww7QR|c>-A^wmS9zi!*!j*&CiPVP+;I8D?ezI?B$%(j+O-G}*$yIN3NAbS5O!Fhe5? zqD(V1KxhD+PibhLWRYTOXabs@Nd&cwq0&aC;FdALaD$s?WNHRl!(g6dWNcNmID-c0=0=4~Lo*!^p^4w!reFgMjgU@ zT#;0o?hFc?ywX(ay9LQ`94<;KO}E2pZ+-#jAV4cC&~X=_!F6Z^A%`lsrp0G2_~_b@ z)S}$XJW!(!C^dz_v)B=I7Smdwrdb-87@8UxCxV*Rt_;vqAt51Nl31K=WmTS-oeEk% zQ68R{omvzY0^0S#kdm5gl$KM%kXDokK6*1Thd~XZ349heRNBg_AhD=8)ym2xH90dE zq$bnQ47)J1f~9Bm<~{m0UaosSC(2-Vr2!ALpTZQKZqKX z0veJ4vB*PG8#hWvLLJ8rT64%fR-BC4!EG^vo@QCmNh?z^cm*7GLP~ZW^R+wz7g& z($I2+=t_Y&gYnhwkeGp_4sb<-QbdCj6u5~2mV?!x;G#M+kHIOwB!j^X)aYPvN=(ki z<;&!Z#LPT!>deeTNm@kv6J|0@19W92B(O2OfHPF^1q8SPLG~|r)&y=85jLZ0K#yvY z>_%=-!2C{ufzWJAyl2ogKx=tWt_3gGgXT1hkY`YX%7E)W*hwnz;tCdso+YWU(`Yb^ zuwzhDbIhxR`5LAL8sk`v2Wf#Mg@VMq%w#JopZsL-y&o_`G7^h3tgMPN3#_a>5{on7 zjTa>G;LHL;9%ZmlVO8YRKg+sv^{z6j59ZcNw_q^h-@m$${5GXlbd`0v=6Z z<3X7p?oIUE=A2)UnTmD!xrLn_gHL8&YO!NprEg*Z%r)o+pyhvXGZMYcj#Bm_nThI7 zummn^P#fo<&K%e#XvG5dCrT?B#Y#jYA9Qwjabj93xQ!l?A6!y|%R+>|(0z>R6ws0i z>hu#(oE(~m>P&c0p}PjT5`u;Xx)O?OW0=eE8jJ`qaBP569ZVxA2f{kj;2IYu0auDt zQ^RXxoPI{}5b^ecD>9rmgJr2}CA7N1VI@@7j-eDX%n_fGSdz$)m{X9En3P(Qnatqi z84~Oo67S;}91`#B5$fk1@9P-ApaI%5p-^gp7;Z6#8enJyWq?gGG(Z?-WQf%)&^V8g z5$F!UL}N2!(0NFft}t7{76yZDgss>iY>%Ni)ECr3ap6A;GnX05-Nv0`1&MN=>n_ zOf<8AEn}5c$MheCA;fJ=5eQLj$8EbMr(~ zBa_rL$Rr6KUqal5Gt5#AObpB{LDw238ziSeZb=|$8X~09Obinflg*7RQj;vrl0ZYG z$;kxLv{7<0?vOGwO)^L^GBY+yF-=NJ1g+1;pEit=ld%U=ijjGWacXi>N~(#aQJN8S zl_l;d1Q~=QT_vU@r-4SpEsawX6HTEjbnt|cQF1aOfRYo9EzHtPjgk#5j13J_OAn48{kR6Y!4D;}W80ex8 z!^9NhM9UQTnh(5YVGpUaMAM`sBg>?;L~|2M1JGH;_zMBUJY2||LATi=E|gv^qSlF|%Jlhe%1Op;7Z z2<9b(nK(kqGR?x!DAmZsGR-j6G7YlY0Z)h{nTI1ynI9kE|Rt84?ef4IvN%hDd602V`1uvZ--WN>ZXpVxmQwDdDmX z*;wphX<=w=l46)_Xl4Og69zg`js%zDN`R&oCW(eg=9a0(mX^lG$OpG!4^Bg5cOpVG z1#~z|nz^N+QL1H%Iq0@s$RdYg&~8-*AOCRIAZN#5*Z2@eCm;9#y#}ajqoV*GW3W~* zj@4vPi^dQ`Onu;T5Ry%Xrf61y=GVC0GU%#x6QeZq#AH)LOXMtN48I-)yu-}| zw5HfH%^=m>EHTLpv={=GXut|{!HSGQ*ODh0nOYd9f$ncNbY)1&OwTLLWpK(&H#Vv; zF+jK?uQb;TB547W1mBO51m13$YGja@WMQ0UZkc3Y0X|grGQRKFhy=% zLiS%0*y;?9DA4{_vor$>)09N;qVzQ69u~wH=x#N%#gL#uBg6_d=&CHxe)5uh(71bI zNq!M{ITmacBxoTS+L^lK-8EEp>+$_b&#KgiN&BVgM5Og9rSOz+jhBiT<0~%J2h3xn= zfE;EID&Sye;J}V_)6)Z+p{M5znjr%1=WtYUR>5&y2gqrN@B#5a4nqBHfy1pJm!Y274?8=P#wWyp?uj-_Of*U}vam=t1|7u!ixW@+ zCgTv36cbDHRLev|V`H<#6m#TG3?$(ZKEx!|$jHpxFbQ;APMVo1bYCI#z8FwA>FI%2 zX@buvpzcLRmf-Ufpa<%MjwLrRN;Ni2PEJls1+B<~%i5G08$lBmC?K$$ArX>~ zya5|$;cb~)ngc1DK_ii%o&P2VCT59dhUS)_yJg_Appx4HvW6ODA$-pysK9`F3%rL3 zTvnm1r3PhLVl%LXfw`qA=#Y#w(=?+*(CuU33=E5V=$5@wVFV!G9#n>R(z}Pg&$N+TS7)%o6JxCZ8loXMX1x*aoK!=#6 zBpOBMOfmuu*h4N@iU%#Cg-0Ja;E>x&q@HA* zmTGKilxA#@Y;Is|Vg~9qgAZOpKHL;CM+(ZvU~4h%b^#T*R#u>L)XK^yKfeIHXaG{W z!{*M4QbC7QfzHYW(J(nhJID!X&@&4`=3utPC_j7J0^}o*hd?V7O%jb$3`{JHP0f-) z!3){*2a?3r3^O*eqrPtq@tz7Cl9-)Jt<$cNJ)Uf8WS(MbYMGK^U}$b(V2@@G&#Q%d@_hZib1M*T3VW^ zk)e6AF=%Q8EQ7<5RPqGal>?B_h&+Kd%_7Ah(ZJFm*)+){)e=-gS64%iqpz*C0R=cT z7C=RUl@+e!ggAE)!2vE?I3wNGFW1NpevBts(;Lo;I&&=mqn#-J-5GK=FO7p9>!6v1AF zj!+pwy8CE7AW#v3X!{esTmrdI1WKAX`kjuThCK+Q+(eORnqpy^Y+_(!k(Qd23~Gsi zF5*Ex(h=euSP=|5+OZ6^nL~ERCB?`x#Vj$=z#uI#%_0?gA2B?am_yf@n}U~_8yci0 zmc+x`!iFg+X^AGOCI(5ShGyoVJL(bApyC|5-5MO_nB5*?yR)Eci$J48#>t81scDv= zI~h#PQ;_D`iAp6yC|_d zt$?98Go2wcuOKlwJ2eGVDw{(51DYwrNFd;$D5xX!^uYQ-gQn%+y+fej10SxOnx0w& zx(g;h1$5_=QIdI*xtXDHYKpm;u@UHq_RPEzBqbm_!AZs#_k5it*b|^xInY>_g}IrD zsd-|mg?Unvsj;amXq#v*Xod+qQwDRtnF%;LP!c#f?oUlKHBT}$G&W61vPd(sKw1TW zXJQb=!6t@AN#?1EW~s)>NhyY)P13j`)dD(I2AT>?O188}Gf7M}Nda9YmYVF!0Fz`$ z$*hb|18vu0a4TRyHp>z&W8_v~nFreRmXlcpiUrWALMi4JNvVmc$rc7ipevVQvM46N zDs)g1GcrsD?XfLNjR)Vto{^Z77GIiE0IF=$j6lcA8kwdfTbfva4oSnRk|6-J`3#&m zz`MAx+GJ>orpnMbK0YP2IJqbjyptQs*jUIB0HA{i zFj6h#AOVacV2qOtQ;pLSEzHf$Ow5y^2ed#^BH~;<=&pRU+zl>V!Q(*C@o!KGfH|fF zat-o%$)Fov%|Sf^GsCo`H01RPkl;owNJ=4d%!bA}sfj6>dFk<0sYUsqlmVJ&HZ)62 zO*Kt3PqhFYxs9R%cE@>s5jbE#`xPXK&0tH&xeI7f0MdbI zfG6kY>}*!K76x`hA9SVh8E^&mZ>R;rk0@NuHbW!sEz|g8SH!nOX#)o z`9(&?P-bd;adKh~DB98zlS{yd8YLMU86=w+8K)#ArkH}Se#4;_9qf}^7SPEGM0ZqH01fZt}F8W}z2(W2;%n}mkkwXS%CPt~o z7Rkn@W(J_ka}eu)u{jbt|7>8GVwsX?oMM?`V3eASyvh$$f12ecRzQ|Aq?o6fnk6Th zC#I%ZSem08SqxvwkXQjaJ;BH*&A==%$-vCO)F9OyseKMI&^Q;YD9t=ADJd<@(9kl; zBE=GVWSyZQ@@O?A^&t97Rhb2PdQq9+Yt;!(*1gAi$sIO)MNwbngsG^t3U%7<{4(m7G@SEW|k(Pl{KcI^CiKvRf#F! z1_<d6Ej4fR59ul$xi21X5H$t&B7y zQ&UUOnpnd$%QVo$HS%~aB5%ZhDoVuMzA9f!NCkt05RMYYf}hpct9e!7mLFVLqpJAH<^h!@yWTM z2E2(uilKp}Nm^1W=rUpGl0Y-$8WL=%e^E**=(c(+?F3MPi)1J0;8-*}EKE}qO$|~K zladpS6Tw&Cz`H6SM;IV<8|8q;%#uuu3@t2D4J=GjlgyJrYh-d@$NMDb7Fb!q90M&b zQN+M80qu!{5)JX~9m4(qU8|RDYHXNlY>{SR2-?Ml(;w-eu2!;f>nT9$F1}?lHOX^5j%R_n%V2}h_w`*pWl5CI)zj>X;v(vD}ffgyCV;RlO z4AM+3ERBrKQOb632%t7FkrEXtYl5I{dJE8{E@p`-CKi@S7KWf@n29MV;1((<_{nJ+ z8XAF{f6%A|_4h4|O^uQ)Qw&TJlZ{d>5?vXP6@aV(X9-BJ7Rx#zjLV3Pl9G)~O%0Qb z4a|~~4P6gS)+Hj%rlF@U2o8m7)zt1)HH)c1EXY9izHBM7%B&M6aKa( z^0a!2sYRlpWonv{VVZ%Faf%7Xv^rAEfgP!%p^2ERgccf*su8htLPJwWK|>R=HUjJ+ z{O&P^ENnJ5F-|f|HA=EfN;Xb20-YC~n3Dr7vM^i$t;L~@OB$~zO-o8lOH4CMNwi2y zH8ViodWJ8i2(~)XEK?H`O)QgBl2VdQ5|NE199~3DR2!xlnVVZ$8YiYCS|%rf)=VQe zY(O)bh{yzINV-fVpsc?%Of@nyNwF}rFf}$XPs6;Gl9p>fO%07x3=NYKEzFV)j4e_j zvq2V+wiBcR2PZtzi*8Vkz`c&z+}zC6(l{~MEX5$nC>iBe3NnhqWHSRx6H^n=>9e36 z!;tEcF7apx-f&TxmzM)1+$ z_>z3kkZz)RVv=bhs2WQ%w=^7XMb4ULi$lQU9N;tTT2!N)io zrW#n7C#58(8l;+8rkW;#cG4phpcskbHe<+|R^z0^WCIHWb7KqhWD8Rhq|K9f9ARVx z8dL%|J>v87<8$+iQsYZ967xXgFvcl~Mxc3U(AlnumWi$m*p-4DMz7gXl>BIJYy_HL zG)OTpPc*RrU6l!IXhLc_TINU4QQyX?CI&`ENu~xVpfgUv!`q;Q1v*<1lqK;m#fIF~ z5MP{;UsRG>ToRvOPyz}_a3zE%z6>p~Xf!k_0v%iwpO}{tU!Izp0$P1%lwx3HZeeMd zWB^)?3c3avMF|7c7|110#99u$VFGf@KFG=7CC0^>Nja7AU?+gak<3g@3{5Q)4b3df z43bR^Tp0@Ti;ZFfunvPBVgx6w^FJ2|CNtAT`M}&B)Ni5Ok0=sLu5( z&1G=TFQ|m4Bjns-0_r3iL+&LrNH#P{H8V&{G`CDlv_#%+Z3w!2p(qo45h$)(2oqCH z49!iA4bu|SEKLoN&!T`7E`%nBOhFfn#)Ic!!3hF1Vv(AZmSUb{l4hEgm}U;$Nj^aR zujFJS&?)1VriMu-M#<2FJLuH^GB!#zFfmL{Gf1{HHaAPQz}x=n#^+75EgWT^!M=3Wa6I|k4yZ15C=1-Q8h&RUSDC)|Td zO|&#iG&eCfNHRAzuz;S0PovBP>(!d48JSxcSeT_*T9~DRk97e}NkDrg;K35;Xb9*e z+M*KpUKB`Gg{go+EgF|pECcL(Snvb{Y7z#|_0nh|4NCQ&nqrui3>w=oFiQhn_=}if zgG3W8tN%pMYRE(bbAx0{%M`;z$YlrMB!cUDZ`1tjc<`)ds+mcmNm^=>iGgvVnPsxE zD+5>tx>6q!AUFfdFtPBchOHU!<`0diGJY6YJ346cZghh)J% zM^1@U+Y$(OrKM4#xj|~8g;}DhWumDe(#a3dG)RjrHYtf|DTZlADTbzI#)he&P1)eK zC(fXOj(y}8pj|GFV`HEZq%i{@kSKQLO{%5krJgaQ{M#Ni;JuGfg&5G)YP`Ni|BQntvgwCo#n&IVsT~HPyh_)Wi_EGXTyc z#KkYfD04##lVk&Pv!ql5%T!YnNQp&?e<8*gnSu^7vrI8bwlD#e=ip$2#xG>eF?sO| zNj>Ii28I@3xaWE3uN9FTMYtQ z;$xI#Xp#sj6B9wV`JvPxklGxP?Vtw1Qx3$hrk2L0mIme~Mkz)XsTR=f9;VPJ#koVA zxH`lPbODs1scB-OX_ASNDP+hJ5!pB#h$WXKCt6x0nWdzpq?#sLm|G&9;s{QFI1GaO z6cX-6rl4xlB+bmiG%?8-TBA|yQ;69nrl9Uvatdf+oTWt)B>$1-Q%J&2PBsS}D`${s zV32H>hP;`JD4#+MNj0}LPf0O0wKOy{NK1i+JH<)k0d1-Cz&Lfn;KeL zCYzc;3q-;u!IKOmbEGC&CYypTl{N;gUNC|5R4Df>B+VG4Ca0vPnVF}UCncv^q(Ls& zBkV*h*&{K{+{`lBz`)!%8Fc6wu_nPi3vo}fMUr`nMXF(1QnF%)lto!U(hg1I0Nw41&7` zlFkxM3@l9zEG*0|EQ}13p{LYBGc3q%N-8r;<7Cq`ti`iAkoQ<1a|^DZ~)Vlr#%-^TZ@W)3h`TGXqGUk7A!f%r-JHFflht zNlmdxGfqnZot}W=K(v%mil_82O$MzYHcmE7OiD{lCX_YdCZWZunORCAcv*XrVTx%I zsH=&{P+&cjL~F8%sRihs=@g5kq%>ngNR0z6nkXoc8DMjpC#EKwSb|o) zq$H*pq!^=D&uBv_kUM9wC0@fM6H7~z#1w;+H1JY$aHj_mV%W`r$0#IyStJ>onHw4y z8iNi7gRMz`jxm%{)_4PT5secQEe#V5EzOL~Age40IS)(9H8xL4O0_fxEu^;u?Rq24 zn~=nnVrHCd1~MVd6m%dZwBZIRZOHZ}#Ap*^Bgl{U*rt;kFR9nET(Y?f+hY?%yN{cHhl z2|%O}bG!v5MW$e@K`RVQl9EjgEG;c8QqxQ1VglFXAVl8iu$6JgFZ&P~lV0civ+VN5efO-?a4w=^+L z2Hmy`&dMlLd0@Zk>A@~$0bh@TS%IR?_k(yiR$URC5>HJuwMa~~OiNBQG%!pyg6!u8 z`xw{UlW}e$Xc0RohnkyMCK{TW8iG2hpanezkWkYpwLn{5X9C&QjAJAUt?e~UvM?|+OHBgJ-ot_zy!ZopVhHqH zH25$NXbwzI4`KzLU00yo0^1q@(SkAI16qFp-YpJto+W6|G(IB}d{&T&VTwVLfq_}7 zDQJGc)X0?qE(-|}@RS$oS!CoyH0dj&O_I|r%~Q;bQY}-B42+;BOw!#U1XH1bp=pvq zlCfc$g@J(q_^ujQ)DlRANv6ih=E)X`Nfw|}j}4Iq!e}^UW|5qfmSkvRWN2h$nq&Yv zP7>x?(CzgY#T)4MQS%HVV@qQ*L({}$L&FryRA`qLoY6pNJ=(LO|~=vo${LG%7Cs2QMREJu!cs+3PFpzO)XPWEe%tWlZ{Qy zjX_&DkrhC;jDzlw0?!k}4K)EBw{8wO%)G=9v@E7L6|M@i_%}#P0j(T1voJO=GfP7r z(>8}*m1JlHo=J@_PE9OI&Hzmt8=EB>n3^S}7#gRVq$V4siCn!QX=S90*DmcL{zUCfS1^S&(Hw{RCt#CVWGShWDX!V1$4f~6eN##mEhOVd=tRM2(rsRn7FW&ZI|e&&!C zA?UOW*miAC%B(RkF*Y_aFiA2tN={8ONd+B!kXlg!om~cb3U)ym)KlP+5}Yl-g*wiS zqQ-gPLpu#qEz*qAQj^UsQ%ww$L3>j2N-_wTPNQ(mNdzB0WNet2WN4U_Y-pNjm|~s+ zsZgQ2=*f;s$ihlcrJ|?jT9KGs5}a9;3R+VHs`f$Y3A#$nD9zN;AUQE9%_uoB#T;~6 zV0@Gx%1RGVki*gu^8LJ+v5a#wdXj~OX_|p$vazK_a*{Fh{vk75i4|%I3FezyCV|Q) zlf;xn(_|y)`8qfw4X62}WCly4B-0cla}!XwSy&iDx*j+qjYunrk2Lc{b2IQM&=v-U z(1qi)@h?iN#n1#4iHRo3rY4|n7sS&RkOS9{Q!3g;%a}1qN=PTBCMBCE8YiZKw(}V$ zLY6J;m0_B366hL&w4~HDa|;t=OIHSn6xEj{E2t!}0Il0D$pE*QQw`0NjgnG~O^qy)&B5n2!X#}#+nMr< zQmw4Ock63QvqS@v#1wF^3oZ+HH*TY# zo+sE)OffY!GDtNtvq&~INlr9_EG&ixEK<`eF{dQ8C@--D)HgFWOiVQ}NCF-IVVY!Q zX%4y_1S$)1nlYr~o>G|yPTG*9pO{yvr{|bg2?|o9OwhqEsfm_GspiH8iK&T3sb>;ws3Eyn1@U zsd?bj7BEaVO*Kh2Nj3(Z{AX%n3A(BPH2i_Qvj^<4kjjEo&lIqCQ3htg`;I}Yp3#ov z0!@dTB%7F}rWzQ6!T{WFHG>SWL0ksDD_Ktuc2_c{(KvFArCFk-adKLsQKB(uR2))W zf+N(VxH2!8?+dgvHZz9|XPH1ooRjkNA=9BK zKEq+XiMg?fg)wO4)il-240Px>=u`kmZx!M(tldCJN0y>v+(5_5fllKwHcYfMHcd%2 zPfAQpgigYP{SWED5LllKHO0iz%+e&u#5^t8$RO3k5_!nCxTFYu3=VoSl1W--9w;!= zjFM84(~JyLj8jc5Oi-2>6_gYi8YSjrrsqNLy$2oBo|2SkXlj%M%6=9mpcyM<1+YUF z!CnAeJVDZREEtv?-J1dmy6YHo5y5tIUDuVjm~v?SAH!z9bZ zv?S1B5U{=({`MsD=1^165%4A!si2!lEKDt+6(eME7$i}E7vV#;$3Y5TtVw~GdeH)D zJ4lMTS*mHOxrvd9ahh=&=q6Q|+p&3;U|DHtkYr|Qk(QclVV0Pfk_s7SM;UCQfj0=c z%E;Wn!obYX(9qB*In^QsGKDhWu1ZZzGED&ujVGCym>VOnS0OL}VUdz#kZNpdlx$>X zXr2V>P=i_(kOCKc(v>6R`VG+d5=wyt4f|lwnbwFB*CzyWAR(yQF-jmpv6&h2wVQ}jF`&H*!j=cQA>G9Tw-)sTMsq{6Bm?6V3)3Xfu#O>Q5*J*QKzs_z z=N8HNc_oRNd62!~@ky27dfwc^+|a_(%*Zkgw51l*^}r7f&0mZhG~Z8 zM#hGq0xv$wkJc#`G$vw{oSK>lT1X2T9Ya2s3neh1VL~wDnVXwhS|*#B8JndVS*C$* zH=~0$2)hcjyfrD+IMvYDFfAE$JOJ`eZKQ!@LvV|f*1=BDy{Q(7spb}Gi6$ng=82Y| zqkLdxG=YL5Ezva1G{w+3*}yC@*${b9%$P<89vT@XCz_iW8W|fUn+F*g-5IucS^kb}z)2H?RF zunLrs21~Q#WK%;^L&KCbqg3N$$nsnqm)GB z#8e~mBok9p=#W3Q+yZu-XI@@v5lXP^k8aknrvZcU;%Chm>MBX>tcyS-^2oxWSV3Mni5V*G)giwO)*SF zDj$gUm7%Foin)b}Wr~5hfjM$k!*0ECb}HdCo(9T1sm7qaB^Cx2pfg@b4Qw-`L`#D- zLnCvewA2(66UZzfo_HoU{aB`$SR@%6nx%rWo-uUSB*a{#bMHWhljoK1c|lZvdYT)=Ki(NO?jeHdM85v!w+X=nz% zzXo=*4u}u#OhBB6``~+v2I)s^FnrW(;rA3N4a>7T73eb+2lGLKy%)He2;>>i=;us6iS!St5=BB2WMy5uF z;3WdcNBZJi9lFZ!1lGJ$6X*@}#Y4MQLE-aG`jnXXA43mtEjLcKaL02$f zQHa${_<4k|bBU18h5?;hVPuw=W}ak{n3@W*CCL@#@EC-V_-|G+H%c~0OEoYxwn$1# zOad)BM7~*xI%z~lgFJJP(iT!!8=8aH4HUl@Eh*K+ z9J!_kE%St(zh!7zl8-oW39`l~G11f{&DbmzbW&+@GH4wbni^1KkP~kB$}gl;fu8qF zp?ME>E)-}fk!hl_nQ3C8g{85Hg{6rRJo~}aAd(yY9B62emS~)6l4h1#gnB`s-dx2nqiVt|Mxz1& z*=d%@v4jY?R8x~glSEUq6k~JHIu5khgsLMq>@X50j7FjOH9G5&CphwrCFq;8JSra8XK6HnWdVUgN8joYp_A@ zK&Ba+nxq(~8XKFXTAEl|f{sW`Ek{~i3oZUZUeMD6&-TKMCRtS(=+0Selwb>J|K_+tcFi3aF1Q(kzn_Ee%X8&6AQ+j6t2Cywq}N9D&DYAqNXX zMZh6jRAOaSW@u#v^E&Ky17ziD#U({{cJLd+K>-F`1%jcQ0onkd59t+roA;wji1%_tm*Ni~J+Q>M`%*Zs&!ZOXw z$TBq zSfuIck(9ykEbvEK`f6Zqo}8R$WMN^P1iHfvd1Dr605Lta1bo#mXkD(Eg;}DBk)c^q znqjID=u9G*B%+y=mRn+6R0`^YS(;mddNqk@$(E)T;0gDl(mWJ}$mNw1U7ZngJfnX(1m?54> z_sh@oD}^6&4Nfvh`cO(8ggDOf2B8FU&K@*vVi-%Xj6pJp%B72uVNw!kl|Z6tih(h7 z<1_K4i;1zNp@~_dnX#dnk%57^1renSvR6R)3iS>YL^(=Qm>1!D^Mb?_&>euF-GXU`rp6ZL7D?u*sb&_S{UlI1gjNC@1rw7kQ%zG6Eln*z zw@HI;!3OOOjK|zmXaj1JBSi?(Ho`P>W78CKbAx2_Gz+6t(21ZRGq7kdFG?-QNlXTf z^@E}-CCxO+IK?2%G6i(2DpU?l8!^Y(CMBC2nVVS{Bqo|9Sr~v;O@hy-MfC%kxuCUx z_%=``T7X&_7KTO!X@&-Api7%Ue!?0qh;_^G7HdvsdLF2|fIYm7O^s8{P0cKljm%RG zEX`A089-(u*AoN`LJCI<@FpdrL_^b5leAROrXYAY66G$U!q?P1$-vaq03qLVp>|VaiXO~s)3<_0q8)fg2a?~@LjP*i8*is zaIHcB-O^EMv18@h9<~YmgCc$2iZkyoSI^hWM-O_Xk?aRk_;MX zfVJg8>oyEcL5I-BgQghb3-TdbFO7^+QjE-!%~C<9b{QK%=0vE!jmb3GGR4>;*&xlr z#LUdp9Idu0DYCKx6>^?=DVfQs#fU^mpz{FP!(>neVJ~!1aB^x|N}^FxvWbNy=#~i+NMR2um+>DaA0OohE(CGKmnD3;jiGr)s->}! ziD|Ndd5V!mViM@+dc^uikX6vTprEG(>*=}W7v(0Fl%!%?e4dk^m#U`+zW)ro{t>(b z2{dJ3V4jwkmYQsAXl!Ym1nLOGoQOQo2QRNvpqrsA49t>EL7Te`Esau5QBEQv<+=&8 z{Is;B*RprG$Zhd z`$$Ugcox+v0xc47)&N!PMi%BtsmX>(sYz)T=AfAzRAVe4n=H{5X`!|qh`WFmyu>vG zzWmh^ezh!USRAxH*E|h${ep!N*~1ALWG?~Ty^&Z{0=@LR#fq{W(s)doInTes9NfPM7 zcT^=f3^InODo%x)18Vl9T38wyn3<&{gX#qo75I(G1s%Hp-U@AzWRh%bVhozFwlGXF zONMz2ssdz;1@dqLs1b$O@0e7YjyV_zie$v)2_PP5WCyakAH)KcMJPMT%u_9m(#(=g zOf5mhs{xL|B0~ej20%k|j9r`1Ldwi2$<#bC*}~MqDA6*}EY+1ECp8b6k+FGFYKm!6nwe#a zA-J9cEfS_dfWo?gCaD%liN?kT$)KK%r5U9D24^2!)en~Co+;+0DV9klDMsc7prtjC zRVZeV?LY+gFJmt}K$nan7Z8?4CKkrVDJGW2pnGCKOG4nXcw-B?vrEM(KqWapueb!9 z{6fGa=7@9vs8Itsej1d+LH&)?6mvskLo-tg6LV8Tlxuj%tHTmal8w@gO$`mrEet`o zV4&4uxv9BUR=J6lNvZLrd6{{cC016xAYLe#1KQk!lm(Pm}+5a0y>wzB((@635`BtYoTDrvy!6ckgSc8H-7vb1?xE~wH3S#4xuoM>rcl#~MUvpHzJB2*4)b6Rc* zbUlj?=$ON|UvP|_v197!z7EXmN*bIB|N8HcU27F-dV{&;sXt1&|M{tO%zw zw5b`w8tfRfa!X5yHy^w-<@rWhrsC8b!J zSQsUOHdun(3w0_u)F9K6B*i;YrIMUm0KIM*vNa#AutVF`1}d*GN;}BrwKUMFCMjvg zCW*#oiAK;1lIVNwNQ#+hiUnwil3|*WkqKxACb&fg4J;z#6*XOg%VY4_IC^?PiFxU% znA=T2VS*C9;8@Yq18+5iZ{|Vk4S|bCul~CKxGLG$C@Ia%$jH##FwGz(8G26~I9PDCRiS64rI;j{SeTfk8YU(u8(JD0qMVfm zI@JY!TpH54S%?z}HE|$&ufdl{S|*vBr&yXBBpDijMm}wYKozyOFoF_3gUo<4M3eC%aWqX_@cD@9MI13R8zwg zi)3T-Book?yP(lQkJnIUuqG>!HeB0NISS!rpODXGRr#)$?d z$!UhjOU6;WX=qrKT9BGp0^0|hmSO_BlRC}9!r0uv5VQ{#Ngm#?p(Nr#^TJ7{X{n}( zsV0dQpxG2UMSOB%l4-JmaWbfIH#ab~bY+0wrwB^qu#I4l&7$C}i4syoCkR6m1B+BM za}&_YMsvet$N>`rpCHVW6O$87O)V@FEiIDGQI3=?E-5lE0FA5`i>M zY0-eX1*pzI)F=2t&Cmoqc$ZodpInp*IguvWz{J=z(Ja|8(L5yueAgAK5_l%XXReV^ zQEFLgQ89S&K5FzPTUeSHm>HNErW%_h8-tF`z^;@id(hoaxY#mJGqW^HO-%;vUpF;I zKGOwM(nBj7%=7_{5TpETf)<#Ym|9vS8l`|{f|HCvtF=%zjhmV1s3GkfH?U&}E-8X# z28Pg*Gz*{n;$oybKS8UvO-oDCEWqdS#g}KMfDe~5GO;u>O-VE}F-SDDG)=N_Wk6E{ z3O?%0@*9HYY~tZpN`odnj6w5o1{UT9=BXwo&_PB^NShd1w}R%@b3i`P(}R1;IioZ$ zyBJhklUL1x$K@=F6Vp=T)4`J-M#hQBmIh`91{Ox8P{qqHQ@d6byyKmmX4Z88G&E9NQUt9sus95yhCw4_@EOD+Lj%a<1ZW-vG}L8aVPpwf zH)E1$oMHr8G>wo3B?7n!@HrX8CQftE4%p1pV$gNZhN-E^X33yyY0{D{Ow(K$pmN}) z637k&O(r8vBZC}=#c0UpT9CmhmgZ(@sirAL21#kD7Dzk(ap;8Z3a~+eD zQ;Um1eNN1boR*kuZj@|nWSnA>mSkc8S)+uMkwK0^UCvetY91h!AfQUq$h4%WG%p#n zwXGx{QmiNCm*%A;7J&|{16`bDZfullo|>9!lm)6?@U%_+$QMYU^T0Hoyx3RGuMuwWJ!s9Is_!3Sl7 ziv`G0x~WBm<{5^GW~n9?i3S#FiDni_palgeD$tKhfb3m#qsYn>1G7|vB%?G#OY^nH!&*SdyFp&LBw^#wKQl$tEeO7HMWCpyNxC6`;A>5;ATLN)FI< zX4nE2R_zlVvTzMZ3x>^1jZ=**%#4i9QY=zZKvyh5#=LU!i%W}Abq&CUE~F4ZYKB|D zj50I?DUUBo%}Py%ObDbHn^+{9r=(b#7^hj7L$0ib%EQ{7poJe$4_R3ur4*Q{Vy`^nSZnae9^zPRL$j0=Gc%L4B=eL+BQt0t z5S-8uT2M~HHUtM-JUHACr(l~}npv1wq?j2Qq*|mHSs+bVgTu-g9BGJURq@cy0%%^w zBF!QxHQB(#G%d|236xYpBh47CP*4hkE`Gu+MnG+T#QZwxXV@nhB$=ClcA}bFm>Zjd zRv>^k4Io!hsOcRk3gC&?2XsIx?9elm!6#I`L69yLTpws27#uvH0eR5SsJUgLg}I5L znW06ZQL-s$u~uqDNjyjvIlZG9gOWDky=zl6Rfc9@n;_)`*ho;_ZfKThW@-jHX2Bvk z5wsQ%T@kq@hM@sQJVFZ`$dX<1iwrb>f${-Xzoi;kSQ=UynVOlH8Ki+%5@Y%evy8=6 z5km)CQ%w!c(+o^O3DzJj6}06pH?aWH!h(%~f)-Y2B1RLy|@*HPOT{1?jvpeDPrcs)!(K?vUC) z=Ab>cNd}3Dsir2$hTwBBKn*1D(ma?l_&~&TUwF{_=@RNV?$HJ6wuMw2` zT}C*PmjOaO!P?cxFxAv3Eji7?GC9dC8Csk}?=7OBc1<(1FiJGDOi42Z-PUOYIad_5 zBZFGf)3TPZOiWBOHcK)#HBB{3Nd-kDD5^kBRZyaUrEWu_DuCn^%S2NHQ1{L>(cBDr zVD!M3-6UlA4zSOU|HGDHfJ#2A~Qx)i^oL40N9W zx*}Snc>|+V;6{l%xJFQG)WQ^WCQx#!NusGm637Nvvcqi{ zQry9V4SeX6NurslrKOQ+s-;nqNt!DIJnrDih;S-W+#x&EFwHp4)HKc1Fxd#?RM7kq zEbidS;HDF3Q5u>W8KxwrSXdgHfo>tU00k?wMF}d>t*k)pNtA`U*xHt$k{?(7X#~H^ zE~zw~V37j4ro_+$bc=wQQL05U znYm?}d5WpADfDVkaPq}J{b!n-k(deECvBQ)U}9-$nUs{6Vw3`&#RSVh`!S&IqLmeF z^w7#GEeA9X<(XHK51Q_P3S&ft9c&*mUUPjxT?FucD)0a?s6Sf_ z+XIYYBuoOX6n0x*Qfaz#eqKptUTG@wEM!S$ZmNY#X%S>{!7w=^H90#qB_49sqOp;I ziMhFfv1OWBvbm)hq}>7zO};3AB;j#54&qmaU9*rSpC#57Mo0*!LnIsz}TbP?d5ho~4sf(5j% zBh4Z?33LO6nMo?rL5a8=4_WzZU}%|QVV0Z(KJFqFG`kE-(kYo`phA^wr-D`(`-G?< zI~8K3rLmELk%f6ml8Hg0u_3Zkam66Y=a$*{2^2IC}sj-SnJ9IE1CCMz! zG}Xk?+}y&#)D(2lHfS&bJdp@lq>ZhlH-`?%Sr!y!=9MHS<)oT|0|+#pmSk!HT0mxz zYLRA<3c52Mx;R*))Ivw0)It-!_!lOC7@Pp@ptZ=$&x_A1&B-w|gC3#)idygh1NfjE z#5{?id4{DyvT>5BL0VFhsbQ+Afdy!sxg4Rw24RI7jETDG3?>R$$%~{OR_R09E9m1T zm@OX5WV7VNWFzxr;}iqXZQ~f5$`CfuZXpqPY8%wwNH$M3v`hgVmIzvx2OYvg^&QG^ z4yfipUQ7$#p-6ZFjJWl*7G}mqNfwDl<_4haFpw{RA&@i^K}pEcGSwi>DA6(%G!+Yr z6_`d#MD-84(GQ$T%)px}L5uec4NOxkjV+Q=jZG3wjX^yK=fu3c{F0#5#GIV`WY4?+ z(9UsCb_SQQp!S`i33#@*xTGk)C@}@RkO*|lok?=4Wm2k%fpH?JXhl^5S~LSn(^08K z`JomJ5Ent~YVdYNzx+Ii93r8^+;0j|4Nu{w`PuQ{S!lx~BNNbh3rQ(yX=!Po!6UE? zR%4;ls$jcNt}RB`hr?@$2BsD!7AC1CsfLD0NuUds(Y|i3Z6@7Ure~uyO%sWDs;JzSuBKGD$H64J9UybnT5HzrKx$USu$+V6;jNBY6Y10@Kqn4AcaJG&(Oly(8Lh5mN3N_bOH|UOyo&P z^^urjVF6lbo@8cemX-)we1{%;(CUMB8PN!QRA*YEfpL-rXsrmeq{5XEJ&7(A(MO>& z%}hX@Y(vxhvecrqoc!|4y!3czt!89sY+z|@Vrpq(WRZk&!q}nw% zN;bDhOEv)=y=7zu-QGg6yCFjvMk&T-rm2aBNfxPzCW#gXuCPY3rg|P{qtU`3!!O{h=NKH0LOi4AhOiMIPNdfH$M9(U)YLa$& z#WV>#N|TnBWNDZN-bD#^1J1mHztYMyG_rul2Z#;nh+t`fd&0a%WQ-)4nH#5=nWrTt z85pEkn&F8N{4UHb&4~wZW=1-0B(F5rEGaWRuQb=fDKp*JsKUg+&;X&(62#2R%L6YF zG%++Yvq&^EFtju^PBuz1abh#me&F*Z*&OSCXEOR_LZ zGB!jSOErdXcQuFj30{_hn!S2@$lG20i@;+Jpsu_VsJsS`=E63=fV&kW)3~gHaAGJOiD{ON-{;BQowJ9abgbWy3v$m6H{}uL<3N@k!qY`1lmWMgDpE+ z*kK#}#N|iO8F5vqMfn6rzs=3k42+BnO_Ng0EzOOQFEb|We8@m6K`Ts+jSN$g4UEjv z5)&;_L5CV6Zy-Y3JcKO*NZu)%l$MxkoSJHCX=I$720AknzK<41$U?@L;S&qc-XJIi zTUq(!=NAN*B$lK?_BF%iqab~9#CACF&SKDpIPg#xjdKEW0!%YBPBKX{H%~M*N;NkH ztyu*5HZ4B~b|eEzsSaAb4|XaQJ&U?&9u#WG1GpiH*{R_fnK{rJ%vmJ>RC0pLQ^DCK{R;nj0sjrh;Y*(EJTD5P5DhD76fG^dsdg^mztoO28S}@WKXv zZUJw$1Iky(EKRL7Zl0 zVPKY&oNSV4W@2fG+`l9y@=ZYfG|(m{Lo;LW@xW=8phK1*r%D;xplmBN#($knlBKDU z8E8d|nOT})GU&!DvynS!=*8<~M_I0PM!3tC`r2r)*=I@zX0G^X}RB={;v}KWY+oW0=8XBi28zh>gCK)9gCWEFeD_|FhAk`e8 z6J|hLzd$a;*boMe5JP&O18!oPlwz2aXp&-SWSC@R3ElY&S)vPCe20Fz7I>foOJ~Ib z+!X<(Zu1Ni6AL3VGt*?FR7=p+q$Y@MBarR~p`Ee@iN&B}&f*J7KzqY1lPr=zmt&e5 z8zvc|v<)GqLAR@+bwfbqJYow6_GLn#o)~g(B)1fD*d2J~A&z4=&5evKlMPH$jSP)U zOf1ZxtH8+(FUXKps!3{!XX*k{z3*0j<&V{so6BAP`)65MF5)D&L z6Vs3nB*c}IaCWkdQxg-@k_?OuEG$h7O)MeD64JG6on&f|2)dck!ot`%Ey*0Z(+pZO zB4s7eEG;<4n2;X!hUgt@lVs4L%w{QuCKg6Ume64{@(oVOEQ>En%>^A5YnGg7Zk%dq zX=0ROYH3bH%)6($-(i-F;Ep}0M-N(#lbl-s+Eh8<}?yPJ8RNYp=k*`We6$AtgOIZx3YqG9oojgtpJklKpv#k zU`VQEa;l+aQi`F8X|kocImDm%Z@wULi3jPM+LFyo3{s8Ll0bLw8mEEIPyr2kfj50Y zgA^@$>XbsJxMaw**lk$Ua~ybPpIXLY%d;ksq7bd+059i|n+K2$ z0#NU#nWlk`!AUkXut+fgUy+Axzy&|ns5k1l=2JyT7Ljm(VFQj$~6 z(#(udb^)PY%VG#QF#~-5I4I4VTAC+Y8X2Xgnj0AyfF?qa&q^-@DK&Z zK$@5vBpRj|g7zKZHQ3mQXoD@y(o#$lL1UV!W{IHFMerI7ne!#a#l}YFMi!uDhNcEa z7RJz95y1sII4B^?&5?q_$bwiWry3`jf)2JbGqA8UGDSW$!lbw|FWD@yBtJJZ+0YO| z$LHjOukuMYFikQ}Ha4?NH84vuO*C+2K$5osEf@*S1D#RioSKsZ<~o9n@dTeH40boN z=@u|q6R7cK$w{Uu7Dnb~CPt}gmIlZv3N=YXkKG4vCyCEUtpE+CS)`^UTUeMIq@|`M zTN;|dPN#>fK}p)6;Dfe{;lT$glA!ljf|EPA65MzWEaWg_T`7=x5F zGfU{qH`sfiY=d-`4b+|BO*K$=8knbgN=`{JGB!;%wJ=RiGKOt^L|IyZ zH~}2RG?cM@gc_Wugc}&An53pAnJ1?vo0u4*6xGmh0;O3Ja|N{R1IfeYCgz}>WoG7< z#%6|w$cw*0d6=BUhhluHWumcRqPdxAa*~NrY7%sK1(be?Fdil7U}+o0cq7XsOG|SD zqa>pgP$o4+j7%XHu;|6Nv5C21BIqVNqa@SBG}9Ec;v2Nq4Y~XV@u5Yup&2~yBU@kz zmxdML28ITyiRPfQC6W`oRn+;I^YeF zUO{~c=&9q70u`-32KQp1!y&GPC`A*<4elW-kTb_kEKN;P%qJCCw6a!HtnAsF8%yng*@SMT)yj5FZkEp2kLai(_LWSlk&ICa0Mj8Cx1z8krcI zgGQmzJx)g4WkP#KkV4tiz`!Ee(j+O>z|ttyFc~s9OI*e?1ufh$H8V3vN=&mz1C8tA z%6PCihA%Ecv4G?f6U75brb%XrhK5E4$!UqHMrP3AX=sUQYy{4-M3$JT24+U7iK$6u z1{Ov}Muw2#P7_Gd#kyn;oPe>Hm?`F|7AA(4mMNCWriP}_J6=G=0VO3S#N&_>)50h< zH90B8JjEi_D9z9u+NuTH0If}NmYAu@i6%)#i6&;Lrim$*pu^{IMI0>ikQ{L^jo{#i zwSiDmUK;35A=5-t6N|KD3p3=S=<($`6BA=o(5agy2BwCo$)KAdkaHb)@fuRD1M?xd zE);*R3q|BQ12dx}BLjmpir_(#)ig8 z2A0Ogi3Xr!I&oz@M2QIs6`0>)e!vrXFpa1g4?1QANqnY>#+Jq@iRK0t7A9s!pfL=P z`Op#*+h#u8ZA{RuhiR#)iN;CBDJI6|(DM{dM&=etDWGGc4U>({QjtfG z;7fg>cfz4HBk;8`As&a6m}!O@mgOsEsV@o5@h7y#;353HIYoQj<*6j4czBj8l`K>z7b+1UTNX_Uynp8EcOm%@Ru! z3qw;26JwJ^qf~RFG-y8*v`>oR;A60qBGh&|#d0mL{f2#;Krf$skW-`qm~5RJ4)q19Q`qWJ3d^ zWaGp%%QO=cO9DPXw*=JvB_kJs7b+qpk7UEt#3VB_^Ar;cW5Yzyu2E#4fGojt$0gPT zh;Bnts!6K3d8&C@vRP_!D)Q-6priz{0cS2UFavF0GfFiyw=_yk23>oGD;GIs<|P(Y z!j@#1L*_+5ITboY2JTEj6oD&3l%5;7L;$P6Ir5waT90X#l$4TeVquVka(*PVn!xD^ zqokBH1B28g1JL3#i{xb7BlP~sC5bt3KR^aV(ft5cLzWjzlZ_1wO^wYgQc{deur^_F zdLhZc(8AO-)xyFe*(k--9JI9B2f}a~pPXWlWN4UdmS$#|WN8j6 zXK_U{*!LuwZenhhnq-!inwn~oWRwPK-QY4E=6LiH32z!rwlFj^O*2ga-E*9noRUH? zdPy?f#3Cux)ZD_Pz-|^I>wBVIfNPp$V4Rd_nq-)q3Yy@9j{ktNBhp9<*18#AzX9YS_~5ibnn_Zk zu}PwZnNhNdA?U7DkaNLS6qFQMf-k8Btr9kdbbR3L81%cV-9uE&^3&3a!Pf(snWY&S zSy&nvq!<_)nVKR`F_CI0^x!$mlq3VtnYqbn<_4zV)5So`Ss?3DL5>BTZdk0R2R9!2@Jc!&?!Ux5CEGG3(ZJN)+%U<++|(F+fhTCyJp7s_Xn92VZc5B{ z7S249Vvu5*W@wmVZkm{EXktV-gToUsC~yeZaVeImiHR0z#>U2p=7ykquyJJ)SesP` zl$1&EfTf|OsfC4+fvE*({s}aviOU99eU4-Uq#J;3VjLsun1ODH1}_ybN;XOa-F#_o zY-na?Xld!n0FeR@PeT$jXrvrt(g}}|28KzXyDd{vlMGBOjbW>Wp=Mzlj>ThEa*~Bv zN>ZAEk)efwWg2MNEGXYW%_3@a0pV3+qco#rLvw@VL{no!!&EH&I}1=~gQ5&{hnT4) z=o)GxBNG$zBuh|R2qp;*8j?yXb5NKX8i6kBHcbPi%|r{sGy_9(L(mB^P&tsP(7oB9 z{m8H#0JyHZhs?AhI>*MA2A0O43zAZj(u|BuA&U#3c`Pk0)5OG1hjbsDLodH z6j@+hdI*jZh#jD)hxHyy!Ha{yr9@huicf%wqe@VK3Yf14YN>)*L8&DmHYhuR7MX!h zQZP?6Of&=y^qPY!UC<3&u*3Tx_o+h1b(M9UQ8q(p-hbCWcaL`w_kLJyP`goCA=lPZ9 zZ)&MYuA4gy{D1i3!T z98zV1ibl+ftb7v-K+f<~aaO@`6An@mLh`L~s)?nkk#S0LT4J&ZsJKbXg)J!p9|;>= zkds*wl3J9TnFqT65`6ar>=atC0-wyh)MCfHO5el+a6&eNv_BxOgdA*3#M!-A9cN;g zWSMAenPy;OU}ByEx*-(dIE;gA!IoP>%3O%$ptZ2bJ7uAV@9OD+4&OxzuTT=hEIHZ4 zBsn=X(I_R!$if&jdV+8kI7lET?t&bF^*Ckl>See>Z0GHQ${sW1P7Jt*<(XFkz823r zGcSe6lxA$4mS$mLW}Il2Vr-af3>oM#gOrsZ_kr3UkkgetOHy+|2?%`d08yj4uvu>f)wB=9mw3oqU_WnJ-vX8#JrOHT$jX>M35z<9cKV32HPW|?N0lALUuk_n2kFskW?`9RXq;?pk(_F7nhM>`NJV$1n5HDB7+a>AB^jDpBql+t zRZz^LmjP*sr8yC>#LVEC+ zTbd`Q86>8rniwUerKUkz>Ne4!QZ^RsRvO(G4d0?;V3wGYlA4&5m|~HVhBDQZmRo}5 zzDT62C=Ja`KnHZEf|{fzW}y3@VOLSAf!q(>$^h;-K`%cB2_x<00Ubtco?BW1?l3~e zzu;LMwXuTIT}(1fHBB^6PBO7bwKTIp9;%0IKP7yWuxW~+X>ziWajJ=lWs<3RBDk$# z0g1ho$~?5VDG)PS`H3OycWK+YWG|0k3Y2<8yh8Cm>48l8YY?m zNuYTM^VBp0gJe?^6L9LHO(PO?IFp%Cl6jJ)St4pJ0dMCN!}4KCk(Cvwg@lqOAPEPO z5<%gpr-z(yTvEY>CXs2z($p|9%{(d5(9*;t(JUF#=t1gxfCq=bW)a;sFitc#H8xB$ zOtvsEHv~;+fGQ!7J3;YHPTK&oYzU+q)U`D;PD(R2Oi2NaF(*P#zotu5*4V((BF)Ie zFfqm4Fxd!ne-$VrgFFVk_81gVu$TkI2<&DMXjUd1r>OM?sD}o>myfhdtFZPEEG$h- z(u^%lQj!df%+o+qFd+AK0JokkW@rF)_C^G)e}=grT`1==NsN;daPtwyvkH?mAl zHBU+d|`)-jp9^Ul3MIp?3Z8Snp;p(i83MqSvmt&5t1KV zQUr-K8BW4 z8i3A@OEEA>N=bxH<3fWEJ#3-*)M_YRE@TE7LIx!OJw0%`Ab8~{*0RRj(%9T2)y&i= z)gal-)Do#>hP_P=xj7DOp`m$(k%dK?k(r@kGWhmBW5|F8c<~KZ-4=<(@i48aDHdjC zX%?wTpyOkWk!QJ06N`!xE1~^zc)tSTQzSot(gSSh0CvobaS~)KHy(b~9kj)enrdm5 zmX>N{ZfMv>31s{eb3_b!B6lcw z-3}-K30HpxiH62T7HNqoiN@xMrqFAbP(lto)|Qf*Y?PK$0vapKPb7KREio|}w4v7= zG)J782wj7TGFA=ByU1gc&iMtIso*0~NvT;=jg2kMEzJ!RlMRxR4L~ce5hX7;VM6=6 zr52Fht~qkeh3*7M(F`i2K!eCQP7y>(u!uqAq+~-&L(pWtk)erU66kIoBxit{`;ZbI z60zXo+p`$CU`LUF7wMpp3{&K;B-n0vk|46kPBXDIF}1KTPfJTPwzM>bTxCHANhYS| zhQ=0(0K@L_5 z4LXoNk=wISe-6zy7i6dtlIp>;50D`laLywku_c?Cn3x-w7?~Mcq$HU_*MXbi9?MCB zE`dg=tigedEfrWA86_H8BpM{9S|%B!8qq&1r5Pm}ni-gZ21$}EL3bg6or)tp;JbSh zT=RhjRFHxhe#Iu<{;egxE(SCSLP`$erZvGnz!uoasTQEa9@ET>P0bSxK<99Pl|nrW z_W-sLA#kdJ`U0*Hn@>UYI^Ko|axTU(-U)UUHV>qjn;KXc8l+g5B&M2~8bh|=85)vO zR1sD3g31BJA(9{-Xm%XZX92N56!oD&vt(kp>xo zT(D3*A?avn>Of{qVMlvGBMa^DWerVOM;qL%0e2c8tw%#cDknpbpGgVCG$Vs#3*)pT zOH*TmBnxvhR|d2|gr_@75-1e{$^c(_Lr&hHY7ud6EJoExY5=8zZul_;o!M=fWNH9D zxgUQjgO(jbt@;8*5&Q^lQj;O}-NcK{0D& zkYZ+OXkuz?Zf;_h3c80K;$!$B@Dw+FK}nCoVia%b0L_t*+8qC2JEoPPn zOG-AgNQSN#A*~Sz?t{a&m_fUa1P?AuHMB@gwlGRHGzRtgKo=fEVg-^=LG1yOQ@07| zM00``8-RxPjSVaQX5m4cm&dJsEkaIe_h5`2m-hz)K- zL&_QO!uzz+yky8~E4Mt~QgG)EF;xH``wMbYfvt`NujI1;c@=c(HE8i>a$;IqvSq48 zQmPT?01j6Mn52ycxZ=)8EJ7+|2F{^ohGyo;#^x!O$!V6Urb))w9g3Qf5U#~v&LOv} zEG^P3jLi})OpMJ;3{yepLqL)iJi~(`88$)!X`;f~L8z9KkPr~=G|o-UH7`mn09`H& zn#VN;o%5CiT9lJyU}j|G$^eyv7PYX#2QqvD6~u5Lq_G7_Ub(5cCMAj4pj(g(k}NFK zlFgD#jLi*FlR#sNAPJ};B%6%uzBQO_riN+B#%V^Dsg?$bX5b?{kZi+rGAIwD^vod+ z0=I|pEh{9Z2!kyVGB!3zOiMO3wKOv_PXsS|0-bdU4FK#;vP7Qrfjf!tOdToCGE6Z_ zPDx5MN-{98NP{0`2g>y%q~hcxgX9$B#O=h4*ih2O80ZSz99sDi zEZ0p9lgvyFObiUul1+@kgRz(=;bNo+P|XdB7Aq@adOT=-B}ASlK30=cOpT2a&B60f zrpBNHXVA(fr0xnO(PoJ}NDfK5h+8U<1_Z#h3Aj!|8F_)|ApT;Y)U+fsP(n4ZFi*2c z1C?@U-ogxdNU$S$7fk@E`3V~k0ml~EJyipYRt=7E7tG+msHs7HG|<)xkhfHvRlpl8 zKm(beTRuRwFX+w|BLm|kGt-ph)I@XB6zF;|Lj&aQGI)HAd`Cc=3pm_?+(bw+H?*`g zNli0KPBgMi1znU0awMqzz>u3*5D$whP>Bt$W5DUz0+wJ5jgS=)%!o!NhAC#oriMwN zV;YjpEpRrlK!Jx*4MS1^QMnPK6rO^xB^Y>z5Gk*MtToR~Ey>7FDK>(PNFo|$8Hq)p zVH!h|)Z*mCg4Fm@$nr$c#dU^>mgXtR<_1P4Mu|yg=B^B=O5lM9+TMzO11%nV@1snBs!z$C>q6|^`x)y&v1#Q?Nk6+Dav ztwBM>0NDdj;N^L+MHrw!N5nNm3KU{=8^tsRT}y6koMLHam}+61oaD-Y>`{0k!ahg@ zb}zIw2v-VAZ(vDq`a)f>09x0Eq6V{E0XqTPkY#eR5ome6kvV9eMjB|BOMH|cm8abj zO^lM0EK-w=EK`llEK^KTLj+fZ4Rm|J&^*I3&C(>*)YQP-z%UUsh=I{EqG__FtP&dN z;Zg$w6JtY@G!tVBlSE7N#59bsgC}ota{@)-V}Y{dhlm^mifU4Zi;d0AO)ZiP5{-`I3cE{Bd>5FYgop_D9tp{BrVx6ImIm57`8Nm)bJs0BqrJ16nqA7YEp`&Wg5zL zM<{pMfof;k4xyx)Cs`P#C7GL8SSFcUCL*m6!=EaUM;($4jVx1BQ!I^>4U8B4Q&N&G zEldp!Q%y}$4572FkU#|$*5vjo42;tf&6CWHlaf+WQW6b~i0oB>Ll>0IAPI<=(iU1U z<49VlJ&J*D4G}#$VxE*}W}0M}Xl9UX0lLTrE%?COk3nf3t&L7<$mQf0mlmb!8h~po zI(B0$4U^3b(^5} z5Xi?Qbz=-tl8g;ZQjO9~%nVII6F;!chxprq7~L3yaINDVC7M zh~Nef-DiRg($Z2bQY=zZOh7mLKqm9SF2SEFP;ypUQi@5Ufq`kVxuuy&nkh!9gr!6U zl_hlT#+aB{g3nPkNKLjdGXEzEd8F#)hUwi6*J$W=5ul#-Ll;AUOwXe-E0v z(MudyHBEye2w$fI>Ke?vMoy0q(oP37s1T(s zwD!ffln|x%ElJIz{?b*VI~}Hp$%)ApiD^b=X{O2Mpuskj;KS&2z`P5pdcl&!G)T|_ z2VC5l!=`PFz^5q27pEo`fmgp7S|+8Y8YHKr8kvH2AR`U*;M9_AWR{YaYMg3ho|Kek zY6$Itn5#$ID{8}uN4U;SkQqqzwO^nQwk;mO|It;Wa(K6Y< z+|oEP&D;cb^ByS4;nZSc0y@;o(%j6##4I@(bfX9?o8Z)vVs4R^VgZ^Yury3E0A+#r zC_j{4ci{6m{fkmki!$@lG0PONG~#M0#A%(0CW&S#CMn4V#-QdeQdB_pRT`nKnS$+q zG)++fwFV8%lE7yaL*o{5v2t3XiJ^tDSyGyjrKzzcXmKODA{z}*R@G4ep9Wy9U>u8e zWfUSbpb05HC9x#Y&=9h9vN$t8uh`Hc36iSflM{v@np-wBPRUHqEGY&Zot~7L4!UgE zJSD|6Dak0s+{iH5IMK+J0YwE)lMu&lnkIs812#5FGPkryL%F30$seW#iABY!<}Rtp zpwsD%^HR$V%`*~HjZ%|M(o$2>%#ssRK!;kw5+KO2umFWYo=*A8d6C@Si6MM_uKu6ny zGO1Z9*apy~rDalbvMFes!US}eQ<5tKL<;0Qimb3mE6UG}&&(?USz?lwl4@$6n3!a4 z1{xo7Wq?UiY>lB=aYlYoNqkyjaS7-yX+s0h;zkP#W5Xo#q(tMiG|*kNt_sZmOzS(>4#Q5y7qIf}y05*$2G;}b#aQ_T&`Op=UElg$jEM+8A* zAKNjuB*cEIWsQWApJgy zOvf7A<^~oiX-38dspg3(#;`^|TP9_ug9_I)P|0GFVq|HUlxmQeY?+*71Udo|E(=QAC>!`u z6GQ;0umqO`;QdmVEhB6r8=xjcqFHKMYKpnBg_(I$D(rMaP`eJfK(#DLON~zjopS;z zMN>@-5|fiH5)CX((vnS#Tp8f9AV*?UZ-#~-`OLhs#GK3&Pz7RaX=ZAcVg#DLOtVN% zgy@ILV>iJlwW6dbF}WnZI5QnoEhbxkThDJsfCI%@+ zp#9;P%8=cFGecA01~YR@qqOAYBqIaMl+-lXjkEAn0X5m8xTGktBsIO#ETt$hJwMMf zw=@S{S{o&$8CaNE7=SJ}OHBh66L49O>k+90hhc`MY2db3Jh+fgEe6#%$*C!ZCI-gl zX=WzI#)+WQCQ$MfE|ZL)CZ$#+CWDNzNH(xEF*h_dv@kMHOa{&F;WfrIy(qJ|)X)-Y z3d}w6`2}FZ5-knQQ%zGW%?*=4C%1yDaooOx8fFGH4D2MRNrq+?NhZlDX^CmZsi5n* zaF|pCPR6C6;=;fr(a^*sCCw6a(X9pO{%~v_F#+|5Ov{ThOF+rX%s4ICz$D2Sw75Pw z6?9=VHa+kQ?^EC!Q!UJr4NMGDQ&LRKOpK7HTEQg+H1G}0A$bZ^q@`9A#21$oftIHl zT3RGom?S49r5G5PgH9vBP=%5x3{69dD&30mb73bA8yd&Qr=%7q7iAWJ_OF0)Y>J^_ zBB+~eXl7}V1UkYR)Kx^D{00}0Xyy=IE*e`}7$=%q8k?FK8<{78jy*ySCPNc&U0z&L z6kn8>l34-T7?YM{Y;J61Zk}psWM~Si&{37xWP;9!LG1uwF~&3{&D_k$JjK$?GR49I zwAmcZ7;r}g%@|{F=?@DllkChqP&u1ooM;I0gh{G_Wr_jlj7(4nA+LD?hY*ThqQl3? zEHTB*D8&MlQcaQ!k#DL%+{_0VIRdSy0hIG`CEI*3yuyg|Gv%!2+8d=zUqZ>Cnz>qJ^Ppl8L#INlKcrsTpk9 zBl27mqz7K2r{|fMmY9>1nUs?X_6W-Ab?^irw)sdy zw5vDVK*z;{`rjxvK&o!=#cj|HZ;*;R#URPl#LU3N!oUz(M57!d1Rh@n4@rT#33=(M zpmpS)DHWgq#`FeOOH7lEO)XL_EKQQr3=NH;H;EZTq7}>0mGCoS-9uE&ODYRe;~^Ek zDd;ZV)HD1~XNbemOinX3O)*R~ zF);*^7K0=sneAH&IRXtcNn>@mk%^^Y zQd&x~iLtSnv85U0@<){DB*kzHw}VEIlFf~b5|d1g%`BlchdD~kk&#%Wrw1=1z;!Hm zEq2OH52L zGdHp{HB3n}fnB74oc0-b`v}Nt&SMt#+xUb8JQ-fm|3QpnSdL)D3v0_ zB9!G3LES*k^9VjAS$FO;KYkxhplZ;NRGYV4D zLZT=nAADsNXubo}Y+@n^R*$8bq#2nf8zmbVnOGQEBtcRf^oV<^T8L7KnVVXe7+EAH zSs0~QrllA{7NddNt|lP=8JapL7N`0q78GRWrF)j77C{Gv(+n+)O;Zd^5{*n#Q;ba^ z^M6=E9&hO22zkR);}pZ>v{a*{RM0ddWTFsy%Mv6Y($X^Z^pfMt@-vCO-o-4}< z40Q5nqM4BiWEc~r6bGA$xM~VBF2T)I&|Hb0o=Z`F0jPCq0^)*p!C4q2Tc#u%S{PXv zC7XcG^#vUujJ(7ck`Q2f2lezQI6~6gB+0_Uz{0}NI0X&Z zFQmY7OfV=gq07g?X8GPqHu1&Mi?$(8{i z(lakD-@G8PGABO~~uR7m`SJ%FJpH?_DpF -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 000000000000..6490a2107d5a --- /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 000000000000..1d86ba584411 --- /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 000000000000..e69de29bb2d1 From ede366be637657280ca506c5bad0f41f96c47340 Mon Sep 17 00:00:00 2001 From: Devin R Date: Wed, 4 Mar 2020 07:59:10 -0500 Subject: [PATCH 189/846] 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 cd258c7b506c..89f55986f634 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 cf526409374b..4c89647a5741 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 zcmb<-^>JfjWMqH=W(GS35HG+9BH{p{7!JJEg0dJG92hJZxELH5v=}rQ*cccXSQtQJ z5NVit7;V4=5r@$n5H15VR9^v99-Wqfs)NxWH-UseG%JXK1FK|%OlDw!(F_a~VCrAwl{{RtiS28dJJS|B9nbGhWVj(*Hgdd^-MuXIXL;|0d zq=5VfViSV}po)T^_Th5p0%&-`Xi)frwCd+%CYhP&=cMT7WagDt=vG*m>6)4773b?2 zfzuJle2`jqzfh2Ja5{nb2$If37#P4}Ao+urmi}nTxtZ`xJC?sCk;Uc0>WN1|dSMuB z0Fi_Z3j>1?7PY&Xv5TL>q5cgH^%t11n`6j`U3@tXaUP^NM|Q3>4)+|uVa{o01_mW$ z1EB0m9Omr8VU9Kq{}wY~_ZK4$^P6#)6Nf|m77p`yahU%Bhx<8jsISMNz8i+|_VXp`dd#B(qCm4r%CLHdm#9@vv4)I4g#6RIMe+3SIt;XTbdpN@L z1rGI-aj2KY;m##E%vpfLoJ<_*HE`rpJsj!-aD-1Kj_^5!!#(vl`~|Awv6XM8INalf zLwr6CacLaxS&PH{PjL9x8Hf2?IQ%7o!<=Ls>PvB`Ux>qzz;>?svlBPKKP!q?8;; zyCL2)#5X=AwJ0?`v$!O+D8$z}CqFMWBrz!`l_5SpJvTouzPKc@s3bm~0jw0O!jzoE zl$0Wnu|*{yV+}#ff|87))Wj4J-+-aKD6=G$0V))qoS&BxpP83g0vE|GElI72Pf0B< zDax;8$S+9EGc#cTX@{!-=}9apDT*&nElJMFPtJzRK=h&*2T~kgo`_@tSOjK1%pG8z zFhff!3lJ_WDhF8+Uz!J^7~!9ERe|^u%Noccf!6 z3akgkLa@`o!sr13@<(z}Y9f+9pdnC{0uBLKh@t2PMHAeWU>T4*N)nOc2IL>G4A>#b zIr+tiFa|pZ7Q`?M;2K~~%qdPy%?72X^yK9DVo+)_WQY&VE6>bJi4RIGF3nAabDa{C zvrCE+lMy2BsU<#%dFiEz>8Zicpms?tNrdZgPc88b@XSlgM-~C82ujUyN-Rb(C@3{O zGe0jFlo{ZvgF*V?Jg{kSUQl9YajI)Ya%urMs$gyoPAzc{f-7=QEeT1jD1lj*lbMtZ z@}e0-aY<28az+tDL4I*&MSN~*ZemVmdLBbcPJCubYEfcIYJ5RPN)bbRd~!u%JS3;Z zCzhmEWR}Ed<`(3D@=i`pelkO1QhrehLvCs=$gtehT(F_(sU-!ODGa%}i3K2wa#Hg^ zDvDD}Ao;WmoWgQSiosC{(pyl;kediLFFmy+HLr{zw=@r=IX*tEqPQfngdsh(B)L2V z>YTL9yu_T$s#J#J%Hrh2oE(Ov;?%^V&%ws6dD=5m$D@g+zo}ZV-kXDqM%7C>`6dzxbQyib3 zS`rT~p4@#rot)#1^o$u`EF(QrINKOW+*r?u!QI(8-bBwx&j8FahSLb$h9*dw5o|*X zPfJ_my&ln>F*f}(#j6GSaYe?o#fCj$c)gDO;gfLbLt0|PsQK9nB}<})*x!|i8b zn8ggy4+`@s6Q{9(!p0t|z65MP8-o{=KLae!$`A(SL)^mzcMohl12zuvfeYk&28Imi zcn3^8z!_v41499txByhV0!{otIz;^hG;sq+H=SVy4)FzO;sQ`}R-lP*fQlbL6K80F z*n0v^TxjI=AS?lSIC8!a{*190UFLPaELRMqQ)ak zy#$)L7t|aB9O52m;!C0GBhbVzLB%uB#P31HE6~JY{+)m(Zjb}a*(Q1?fmiNo?;2AcR*sQLyp@e7~tn%@P`#9`)Zpou%| zgP3oCCf<+_5w}1Sp8z%A0Zsfr)V~pE;-32<;aq?w4vY5=H1UA55PK(}i6=nYKQqw8 zlcDBsz#)DDhxh{=;y-YR3sj?q{~xHm8ffAQ2O$2nKofretp@|p#2KLFb_AOE2WU7a zpotqo%`ZR`{|62K1~hS4`k#O%z7A^60yJ?(&_Evp1H%p+;up}wy`kzKpoynK#Xq2l zS3$)YYEZ+o8Y(V;Cf))SS3nbor5_74@dju*aX=HF4>czMO?(SfJONGoAXK~nP5drY zyaP=f=AH#;;ss)ma&HBi_yef;1~hSnm5}tm15NzH4v6>xH1P?$A>tR%#5a^e#9yF^ zCx9km7#J8npoup?#eblQFMx_O)S|}M0jM|!n)m~#xCEMb#7;=Q&_EOa3k^>TG;y9o z5cM8t;;{4+fhNumRiA+-4vVh_9O5(3!~;MRF$@e08_>iL{DFk$4m9x(e<9)r(8OW& z)D1Lo1z7tKO&pefexQs0gP6}yhZ_C?{~_WWXyPz)6wt&Sp!RB@iN`|2%>qrlA1dyF zCN2+c4@aPh!|W|U6K{a_?<&y5Vd^KKi3dR4IRi}`rhWsOI0LjE+<_*Zc^Hy@F5nP< zfkT|39yOd{=18E4w?oZ0Koj2t75Bg)o`5EP8mhhkP5e1jyaR{$0vzHy(8M#2K*Irt z_zN833=OE^unuaD0Gjw?sJI52ILA?ly$(3UBXEcp;1KV?A-(`jJQHf~4m9!0Q1J_B z;upjr2AcQ*sQCtH;vL7J z;fzB(0Ec)2n)nH*IR$9q62~FtH=v2b?45un9syOq08P9AYW@Z^@g%7F18CxjQ1J_B z;=NGu7ii)KA@;ZM_F^j{=(bC(yzV1_lNLG;zJh z5cM8t;x16}2sH5tP;)YHh*zMAcR|hRKog%32(fnon)m|fc+m1JwO5(8PP8=KMethq+Ur1vQ*EL_pjxfhOLN01;O}6Aws)h-;vU!_0R;6TdJQ zqTT~d9Hu@2T^wqD2AcQ+sCWUIctaAz{0cPjx+f6v2{^=8poy=6s^5Voz6mOR0!@4h zRQv&&_!X%52Q=|lP;rJ<)bNLyBY{KQ08L!^Da2nMIK&fhh*zMACqT`afF?fq8N~br zXyWsr;v3M!KS0yr0W|RnsQEY0#J51rd4VP_@&ID*4>a)u_aWjOZK&Y}vsVI59A>Wp z4sj1OahQK2(8Lcx-I;+V-T`%I1)BH^sQL~xahN-2pozoWxdBc50yI2NpouHLfrP^Y z9O6IF#9{eM0J@GCHs2&L2V#x{nmA0o0h+i1RJ{e7I81#2n)n5%dX6sC_)3U{+KVQ> zpdBKvfhL{+EmsZD#2!Vf0zytZ$J~j5DF3RKofT` zg4Cxo(8Lq+AnF&OiC_2v=^w2?6aSzHF=qpscmcFOwF6C@Ask}<0W|T1XAtvGpot%V zwl^N2i8m-g?0tbIe&IL7oDXQ?1ziw(f1rsw=s?Wj=thlS0ciXxpos@S?R7vCU!Vyw z#{*4V0opzZKoj2p4d)0n@dt+??oU7yzW}Wl3edz8q9OjOKog$;TFA=4z|esvt^l2f znt>+%Asb@v0yOae&_Y&F{~b;I1JwN|(8L9x{rU@N;sN;(cRoNDht|t4(8LR%;rszj z+(8)<&Ogw^6Qm&G3_Ymv$N=sCN}!1sK>cNbCO%;S#GMXk;tG=>;p2fOZeR$pHv&z3 zLIA{^4m9x#VG!{JXyOl=AmS^~#1l3{?A?GS{s3yu4m9x#Ga%|upotqm$H6Y3i3e0b z?0tYP&IIw-2Q={wpCRV|Koh?Jbw5KdYWQ!M3Nc>*O+26tA})a@z90@Fu7DoYG9m7qfF@o5 zb>|E;aRtzVTLuP(1!&?AWKob{$)<+Z2#4kY0;TdS+6QJ?F z0Zlvs+TPoNCSCw7mkywbD?rQP3uxj8pyl=rH1PsxKl%llxC1o(Fib#A4{$!3xC3ll z4oy7ZEhKyl(8OWp2teCupgnewn1!fUKoW=SEQE+#Ac;elFoGpKki?-&2f^Y2Na7$n zz=8}63=v4;pgn~!@eCw!Sl31=YY0PK;Z`8 znhIL4iX<+Nq+S6@9JC%5CZ&NSt_TwZjn^TGBkukv?Cdk;HEx ziR&SWzd#b#M-u0Nwg*7rZ-6B3fh2B-Bp!hzZiFPBfFy2=B%XmJZh|D|=*fFusxst%UvKoW;8Wd@5+KoW=T zxdutBKoWNa2|)1)Byo4B7>N3SB<=wcfMNq^{Q?R%PpBA(azGOI1_?m12a>oCR18El zAc^~e1fX~UlDI!q3`DI!5)S|gK=BDAanRmTs4&9=B=I1aAgEv53CZQ4x&qP?fJp8@ z5(llJhlnyT96%D6gb6Y*Fq}XVmqrr5fFusx8V;7dfh4X87J(2Cki>N$LSXU*k~nCu z8dwlCABQCF1`}jpVEBO~?u8`I0IjD${`Er=_h^2@;nB@{)?1muqxC=u)Bg(|%||$( zgBc7A4F65DJd_#!tLAtpGw{nhF#J~q@iRasynOKg|NsB0Ngm1!pk2QVFE4=kQ6N4j zNM9ZR^MgQqP?i631DNjx;)AOCmkYprClDW0#lM^Y=39aIpk(#30n9f7@j=dhSpep1 zf%u@R{AB`|uLRTqV7?TH530IfI)M2g^=T9StAfl0(IERlbd;wu1Bm_vrG>m8 zd@m?{3rYuhL&R@E=_Fqe9~1x}Z1~o*^J1Jw=Od5iCm#Yre0o`I@A3MgMs!GkoEfcVS@g=(XMGtIQDU(aXBhSDC@X@`p#~(-KLKZq`%okSO4d{r@0M zk6#|LkT~`*DAW;rk6znCkiK5iEU+<}|2#UMdK~=6?9utb55{927yp$=cyzNy z`alfi0U3zPyaroEUu6b{QhkqR+jbvi28K|Odw4v$Z7=yKGrZva|Ns9BkN^MwA7lOL zqs+hvat?SU{O7}IVE=%W$G%Ma|NlRPAK=mXV;86*^XOzzd9m%^|NkD{EGm(QVfK48 zz5%U_@#w5k5qROx%D~X!qQc+hqQYr0U2i{nCIalS4W6+W?Qbo~GS-=o(>CBviFM@7P;^SDRnIj|dEn1Sr?tx?H%kpeQI zbBYRRn8l;BMP&kr-wmZ(50tRHW_Zo=T9KdIqq_yJXaPu3?;aHp{bCXm1H>xQhxX z?J~Sp^XPU_QSj*IQSs;wRPbng1!^sObOwUe3xL&uMY>&7I6S&TR0KSlk7yhQnO^}4 zJPVL^i|z`E7pDXm7(6;dR3bnY1%NCHU@Q>?OM7&CaClgI2o#sSjt7NY1Xz@RdxeBY zXNiizi~9l$3~8M%Dtch?v~C|2y|hjba0qsTA~^x{Xbax^Jso!0dgbw{1uQX z2B6Ra`&R(u6%LRXhez`dh7vJQTuOk#Mc~C%MTo%~uX#WUz)CeWR`?Sw*|z?B)FC0Pzh)}GhX!Q^id(gc2LO>@B-AT24yynZX1;sz1$28 z5l9XMr56KGcqo8MWRN`ypzr|u4=E`4K{5g!-5emBC0p#TQNn z29M?=8Ax#p4*vjfVbdnhz`)N9N)H~*_6+<}4yZ8lZ#!_9U!K9E`HhE1CpdB}Kp|o9 z!U}4I#(zkjgWj|w>VfgGse(HS7%aohpi z_47FH0Pc!`LfXKi`G^OSqdl76M1b7W?I!>VHUp2&^C0g-ve=70s6`Tn2Re^|?3Q?; z{pbIGkQ>280ywx{L_k%614-jWCOG}EsDR=RoVTF)sGCRSMJ!0J+eJmgquWCPXF{^_hyv4AFi z1CP#gpxA`^sY@0TcoHD@fh#bB7tiD%6_~?|IdYH^5nLDY6s>Z-MTjIqkL2%_P-sPeq4z9EnLHZ;-bU~rwar_{t(dD6e5G1JJ zVR@q5&$5Ceps2KoqTsMdX zY72n#zehJaQtnEd5a7{W&+(!R><|_eNZSihT!Gss9$@#rXabuZ0!p@^61ulW1>Bqh zmrVjMK+8lx30C5T7pTyJ#SA1ex}94kP|L5((0DyVpI<1eHLDS!#TI0jXi0ZJDcAXNn~K0!;B1W?|M z07)c(BqCm{gvNUSNWudo5df0#c=13CBV+%l9{3cnxg^P^#E!Yn2%PS$Ai2LQP2a|@ByTuvquGV z>KiD@Ie<(7ozv&h-J=4Q?4ANmOk5x#u)0n!4iFoxtkX;2<(hy0|MPRd&_Z_aUl9g| zm%ji1|A&_!jc>pn233)Jpbh`;9-UJlsjU-|+Ca90655MCXk`RWVBmDv0~GhF1XeEs(gE{n3nb5ULuqK7X@kNKluSU) zpYAPCce|)KyeNd)X90>k18_nEO^QMi8pt~yjYmL!2PZU8!h3OF7!;e3ga)Z7U#x*9 zG!2j;;9?Y9v?{!KEDUiQBx|>TvnLk^El5fxB^-~dGlB)@?wuO3Jq><&?J z0L2|RO?PjBW=M#+9-ZbM-CH2)I?FjgY_PJ`ss}5M}^Zs@;Jcpe{OSe4_b{#0$^>HK?3| zkfCxrtQ!YeB;RWcB36L2QpdM9?ip7hhccAPDQpNx> z5!~?tw*?elh=Yvn1UKDXR5&1=E^xaH)ams&4A%2{=QkG>bH>t7uQz~uv)OO9E^e$id*Qi^U6VVQ}+@AL1>K7u|0`-U5wabhxPKd+_h`QPKBkKFH|N{F13i z{WZgDfp0D%EfC007xWx$S!o%w)P<(cR@-jGo^p>b_ytwiI|9?pGlX$`N8kGD%o`z=_jl-`W zgA#=VX!PmzXN2QG@dfTLLYjr(@(rDjo}V!Ip!h!Sq9OnZYfwqkSp#aC)~Im2n2(gz zAn}TkKRr5sr19sorSaFZgGV+&{nIr5eD*Z{dT1+D0n}GEcmW!oPvg&bQBmO6bWsuD z*Kko0NaMeLpwmT#r`tt^2iyRKwP#^wf{g$tcu>{Rc^+&OC@cg}%mWpCE-DiI8Z0Vl z{Po~59!N=(268XD`!qn^7X{Gx6eyUXePu`)42~C&X&?;hF7U5E2r4Te?I4fNLoe=t z(k&=QA=>+(@=~7xWT*qoPzO-vQ2-Z7VAn!o*WiWTOIYj*cr^b|C<6BqAw4G00i_Ht zVnIp4|Co!4J|oCpg%^LIqY&Vl608O!4lZmJUYzFz)$btHh$0wVm%UI04Q_%8L45}B zs28Zy4p#TF`2YX^-$3S(Tt=fk)#Ta3k(`3%H5} z_5VSw0dUK=vqq%=@D=FuSN!fC{4o56cr}q9CyZ4@(!7 z3jU@Z1_p+g!i*qyd-Ot^``slV)nG$iR03YC`~hhkcz~K&6(H|syqF3Va{zI{5&@uY zXT*y)JfPMqbR^jWB6b5R25#1a$222e907}ghM&N0>vU160M9Q#+@=k3Z*LE@2?`n$ z0d-43a^PA5?1%!8M>Sq>LtO%nHLyg+i|^kdE=F|qz!jmz3qP=}ptK7bJ_5PU;)NAd z4BYMlmuwa<)Of%F()lJ9sptgfXYlwZWWd7*H0}n@4B#TaMnwQrvx0JLZ;6ToIBmSx z3u+^FgXFtG%?oVZJhbu7PIHe=r2Zbk@`G^u2Hd{7_`+jnC=_@op778-?y8ou6;nU4UP^_-3)46ftu8y z@iq-An`PF4Xx6{~|6#Rv^AAS;DF+)KGV*Ub2x{;-fZS`~ z!T6nj(g77lkRb{k;K4E<6@@Mk12mM{;iAG0@+^1|Mxe__g}vb+V+W|T>vmBQ=mL#c zfQEx1^FJQVZwx@~#xIY#sF;C@ zCvZE{4Af9FXY}ayNAn^xFNKi;5W|e+y{-66_K1d>KAH z=U%#i=G;K->eq{5?cQ0SY8zbbfz2xcH6+a#UcUqdF{C-X23#LnfQE!YVZm|CMMVu1 zYU&_D4HRnXj9^2c{WGCsE-LEanh0E-zm@_Ah#ELRz_AS;xdT;F4BuQ-)EP@TJ(_J) z7)vC;?KyRZ*B?OXLIC7*(2$|U;cqS~YK$eX!1lg=45}Z%8lJ5O_@_d~ z_`x%A2#0~Q8j`~x6)o6t;Mf5*lGPbYrNN=uT%*Fl04~@(_}wmgH2*?!>OH(pg|~M> z?N3nn9dl6;1BIG6h!6t>oj5G$FkC7Q4noMZo&Y!q#TdZB3`*(WTvWsvOQj(0vr$2F zo-jBh#Th_-ZcsRYS_RKyrd-h*B6`UP_H&4mHAHwo&mb^h=;?hdva)}E4h(GDG5 zg7x1(#Tazh5V@@ljUUjI?J*Y>e~?f8;kjxG_FPpe3@H~mJdQhnZGBw__OU-W(ZHvW zaOEOMH3`aHoj#BWJPlADa{y&4@W>-9k6FO-n1e^Bm%wXDaCkN!2>>NAe@1ZH_h)$h z^7V6Yz(VFnk+R-7kLI5YC6`~{hs7r>{eiOrC>MZ28)+zt|1~%gA<4}e>?L(bBzA+R zIY2?!>7yb6Dp5e}Zb-4G0dkKzB+08Yyng)pA=o`A3Go!zE!Uy>0W$x6tVIR9u=!Yv z%2ZG@=va%&bSMqtGeYG-G)SD$quZSWX(9|%QcIL-zh-#N4;o?t58g1{2bXbBgF)34 zXd0g3C8%2B=Z0AX$^{Z0(4x(OUw{GBx;B7W4~hl{kIo+O;2)^TZ2*!k01c~wi~&Ve z0!X+3Bpd)59}al&>m{f$1MX0Qx@X{Y0M4HZAiKbQT#auK?=hB^gM$YWco@-D`4TjM z_6-swjQlN%pfV7`V|Xb7YKKBR$H?Ep29W}X;me=@{{P2k?%LPKU>*(w4V)k#rXnKOVijiu}q99=)uaJ~A?R^oFPyfNCUg>rlc6RH6RA@Y)Tu z3;|UAb~|u@qC~@^J5a!**%CZi2X5Jy2tyL0hDWmnLkY|4Ij=vyo&gGPaC079Nxc5R z&yA=W?!)>Y=;Z~7&kpL$LHoC$fzVG#<2#sqq$Ve{zUc*zH-nn3oyS3qaETYLpcYjx zYn&Ho5e%O2mu*x4}W&U8AD$!WcXn=AxniDz9o(6ka%iVji>>10(=y@pig^)|2?C zIDi@vQx5TO`_%anJj1LG3P=aAl_2B6L;fBwlAsgH;1tdAT8*Fk1?X(3*IXdgp!uu} z@O+lVi4giL$5 zs6=#zs3g38Nuc}>@HqI01yo^yrd7I4R9-mW{{KG$5q}_g24s0e#V&_5KXz=(YxSjh#@&PElff5>Q zwh=lM)EU6x!3qjY&@hOCN9S?KqA10ikVRSG31d*<{P@bMV|kUIrlfL8y3#>@CX<7IrH(Ya@D{{IJw@PI@>Lo>+zAq!A{0M-32DgrM) zy#=k=fei3~T6I_kWGuk**%GF*m=OCoA(ZE)-PX}ZQxP#vbu28@$sz4(HkTC*K^#dB~fwzA_jX_XudC?3i!NExpIYEQ` z1L}`~R@%Y3TIa#)gF%z5pcsKlffwO{R%XB0cnzN3JV5gu5-*BvuoRTsF zn6Xz$8Oinh^62ItmBniQHbtoUbFrJh3}*gY1^nixVmDt8JiT!Lzo;#C^TS}~|CK>A zKh!bYG0ZX4F(epL^RRn#OZaqWfJ+vR|DwEd$_&1(Z%Yh)dQBPGl^J|`Yg9NqJD+&; z>Nb61WB`rN%Bc8s{`cv8=F|D<|5cCX!;GHINB@^T2W4%J|Du0n!P!$H+A+p4)-ld8 z9(9C_0W??9ZQ%iKSUGt7f8f#i2`p63;n8{gzp5>|dp$t$>#hKgUogL15zdFkw`b=+ zaD1D}V2zJ9Sa|WuV~y`T1!#PqMURhw&JV|3R6xt685;kAQdqGls5EEka8Y3a_jF&( z@?&6#JPa1>JZQ_n!N5?gc6!C1=S)A{{H zqyPiMKG0ahE)W4KEt`*UfJ;YxDNr$Y7^J8~5VRcO{)<27L9Rg9iPRPVw;w&a*&#-= zzAO}AU^pD?(fPZfo`a!OD=jT8#iN@AyqU|R^Porb4$%Gv#!@+t<{dBo|NqZW!tT+{ ze9VD^(W9H$quV0-zpA*jG6T4^kmkXkcQB2=z5%2KF8u@KyzX*v2nu<0+j|^*#sc=k zf7Mb+Wd`tg&41NdlE_7*M>qR1&<^^j6#02R+5 zJG$ICY?#ZrY?w;eqCv&zVV~}50ncuG&u({){{kQqyx6<-TM4gcx4lPqfWUvz1rngP zRJFi9P=gojQLxtks_hcW44$3Gz}m|>Ud#ZkQR=Q1c(I;{#w zqTFD^toMNiF(79BSGAWw4l1yE4`}!zg_NWOIBX&NCp?<%89=)s$^}5_m>;w{4jQ1J z#FZI9`H!6uDUD13*{y0X2`}G0dTl4lfHuHrgTEP3Mo+dKb>MX^K_1ye=9 ze#f-A1}sqw&W$1--PIi6+z2fv;zhvCAjf!6?14kKoa03nc#5)G;6)=hBzPpDjsvxZ zkkd!&{}Odjb|jD~e+k3VFiIj}hMKNikq z>4WRrdJd0nX92K-1Yo6vD9G2K0uz$C;{K~MILGeU&bPzx)>g6?9yp$o>IU=dA}2`NM`2)&HQrS$91Lw6#zGj#p@FL3M(VGUj{* zqW*2oZ=kD5z#XCID)b)ngWAYe5o-nr$CD1A`}M0s2*T zh<|dAft?N?BAWn>KX`dt!-krk0zA6;K?}+~x*0vX3pqTxg*>|b1w6VrLESS^J$_{d zES)bJ|AKpq>IYx2smSvj0Cg!H8lKuaG@Na4 z=*UrF1z&mby3n(m{pDo_28PDJ;7+5?!52I#{5%gp$~#|x^g{cW$a>lKf_jawO%J{h z;`snm$ph^cvLb6^L)OL)x>lIMv%8$bvpZe@w53(^hKJ>gViOO>*ZfWMKtoa}zAo|f zgf@`DK3)#WXPw7AS`U=Ey{`7`wtwvf9x?;>HfHgHgQFzbvy}Dcrf1g>HOx=`Rc#uc0OeW!voNA95mGM zUv!QDD1{;ny94GU8WcpO*W-4eltgrTodH%(a(Yb!Ya=hcs)F@G#`iryc^Wj;jkgPX z`HK<*c#H7 zuw`I?%`bR#+rN}R;)BXp$B^Iv&(7Va`1If7(r5EfM=(Tif3mGsQQUf5qQl4Qt8#pV(kf9w**O(KApe* ziz@RV(qs&{=Y~j()44$XWpMu3fDpCh10~u2q7QjNG$N@&>RZ_Om;k5%R_*0NZX7_z zBf%XN(31Li0T0W2rL3OaY_EfgqCHw~mneC5+adhJ4)Rayfs$y?PBzcZI1Z1_d#^P? z^(`Mv*?-Y!E@cMK<7}WYc+cZ*pdLTN4iLxi8)&Ew((^y;(QDgp0h(GAz4AqgVIOE| z{(sR%u=^4IN(A#gI>F-uS3ZIU&F{asvmdoW0=3t`?w9xIt`_(&It4Tl&|NKn=y(1X zJpmGHKCA#5?O5~u|9{BvL^DeIvuP5JV=4mw*-eLD18LO zEOZtCow*uYxDB|e}vl{|6lj$ zu6Fn@dX^ik!U55Ia{PZC;Z6w@cS0L9A30#HZA6?;0F}AWo*);fk@{ct6bGmk2TSjW zmd$_F4IIiy12g|s7l53JnqJHWLJ%|n z^@t7JKM;rpkI#VI3escX*?G;Q^E0Tq1fIz33{jCl88K?!0XmL?u~gEdw;=;G3DFs& zqTth6q5_$smw8?C;sas_JXF1)_#ICIk)Zsg&9Z~91Sm?{)LFog^Txs&OxYy zDsKY|gUWAk`vo$--}nZ!RDwVJ0BEsDLKkZ!4`^(>w92R3)DJYY-)*~}1vJ91nxV_Y zV0inrAV|Ll<6)2H-~9aTCQJ+rKD|0Jp3O%S_JSudHSg}XV+1uT-+6St0uQ?x9&r4B z&7=9p|I!nV|F3t0HYo%&ANb$=LzTZBbm6yWGE0jO;i3eu~A{COSIa|ajX zIiS5-F)9(D3e5sE;Z@+#`Oc&B)(hWu28Ls<3=E(c5(dq!27oiON9Vo&qK&M|49ztv z7L5EYH^H8(QAzOJ1KMi(|NsAbm+lf38?Z-xx^<7SDl@#60fh%5zkyQeMUU3E{4Jox zdOnsj? zhHeY6vkY$=9`NY==W+0v0!SO@O%KkGpfx_=B_p1(DI}Neoe?yag-c-ya5y-pa|#JfJpNTHgNn0 z&Fn&`#v`Bv3o1#VGe@AUzn$;?i-xl+GrTU}4?5omT)q4k^#u*cw;m`>K^FYL2Fgp_ zE-IYeJ}Nw&J}TCou({a|8I^7q6^rf=uoz=!hzbiNpnGj6TZ8kb_girO{PRtT!K3rh zK9F7iMa|ejCVF%p<`-a9G-G1m7j)p}7hvTEGa2@S##zB5}*g1=+yA&WsQ0da)BeqF-HNxZWfj95EbUmK#@*IILAc= zwA$2qGt)~@yzvXTsBrKLx~MoDcTuqbhpvE&iUTOm2k?U?8G3D}S%H0+`3CGmCKMlr zuqiXVlmtftcmpITz#DCH_pqpdRFXq4b|NpfEl=JAr|NpOz zJvxuR)&q(3)~E!$$OFxucfNnI46X~5{)#zXTm`S{zyIRx4{*|ZVG31P3s#6!pMvWT zlYv=(W`YZH(&`EoW6` z_%ABRpv>@E*t2&ZMAB4{nStTOn>bL3_`&g58^}yXpUw|yj{Ny;pryv(BGR)NtegQf zLvb?Ak-xqVq&toOJ%j^NS7MOn!C!Y6RAi~8@fRHMOY`KfI}Wa1#6ev#P`iW|ROf<9 zAkp0npc%{$p1p0LS`OlL(AYMpp7{4-?{`o=@%=^VHc&8w+K1teVZj*w2c67>&Hw+E zp#E0?rwCg;(B9o%(Q+2B|NkpN{11^d6=VYWKNjYHkeP7*8~p$OALf6MawPwQxN!f2 z)RE}_|4Ix#ogn{f{Qv(S!~fr){@)7qKe+9H?0=8W!(9y*{{R0E^}i3e18ER_7!?2i z!2b8>-Mj$ef6%3b9=)RF%wYfj1Nq;hcQ-`RbRnoLeG>zk_quVc8Du1*N9T<+{`_u` zf;9g8X3&9K;7|a?{>3!@`pF;}@C{^e5s;eCpv_kTpZV)~RQNR}gO2Uu7YtER040kP zpcbkkh;_iD`3F;p7^ou9fQ*)NfvP`^7n)x|P3s#Tz0E5?u5LbJ5PcXie%kydgI|E1 zU%+30Kl~EEpg6yvkBSGVys+RGa902^B96HmFoB8@3w{BRoIr?51ivPW3cqFuXa*Iu zVx`$eg`q?abheQIRwpd`^8ddF=yVk5cs+R9q+EhO`~Ya&nqQFpn7aZ~i5e)0D}oa? zyxk-OZ8w1<^1tfcze=Em7vK?SP!{m%{PyDI7f_u3f3a{gC{7W18_}NkXnX_Oi;Nu2 zpe)h60~Fkh{H?Pt#`aQZU9sXYir3eZBZ2=to+k8|MKz98Ew+CEQI66R)*Uh5> z@uv^}#!8LSxv!N$DT%+;804aE78S#j9_=3BW#Aqj%|Ds=TTQ?cJ}MjyH7WuOrDh-> z!Gb{;!T|*XLy62WM-IlK602#()|bpuMCFAV2ilI%|XKE>Yg+N(}qr7(i#aS^ZLCc88f!bn~?SMUKy$MY9I?}O<3FD|YJ>BN$sA>Ehe zHwhly?EC`m9H6$01E?H~1Lay!1JeOiZh@D?YIuNpq36DXl35IBmlOCvmHUR@{)_(n zsRTMxk-rtZCll2C1I3L1sHD>Xm2V!M&;N_6{83^6%>#5^d(G@>_}{0qMkN5$YmD&d zm7M@eyr3>AKbRq-;?u3`^H+(%v-$6T{uWSM%d^*&(W8^q{I3#d)f9h=BPi$e+J^sC zV)!pw`AdmmKPZx+O~jc$Ans`~1F7mf4C7ruT>7!z@A2fplwNeeFu=N0c z%T)#j28ewM{7^eVi$q|j;el43Oe$w!;1>WD`=I8dfQt$^koJQ{SD>cd0EIHhw3#5& zG*~ytfLgc=KAjZ;KAi;;KAjl~9>*O(K4t)I?d2DAFn~sugM&x&kpy_l9yER=2F{wI zdmbw>>;oOF`C?HP1H;RHNUpa~0p&x{?I1}=URC+6#PGTVIXIbqgK{sjZ$JD3iy*o9 z9$3Uf^9Cr=!FwqJKuHMH{|C331wh3JB+qw01n2pf$Dr_jv=4M9?|)GA)IpC<`05zLlgP?SjUlz&Sy0q=SA(KSAo{=RGf*F) z*Y<`QI2sZjDKYE=9isZ;Uk2!$GmqxO2B3vYoiZx?Q$hQ9dTlo;fmHG?djJkZ_eYRG zghbuPpP*=g2GO27kU+e$8WbF96OhI$n%`)6cK+MVz{J3?A4EXAdP$)E1Sr*+GBJR% zDku|kfP8fr(rnrXQVSX!6+Hk^SrXyX`TxJ@FVKK;^N;`hEk_tZ8jpZ9N_bcv=5L8* zWMJTL`pyViE%AiEMU{~OvMtP~^S?*uJ)h2p|3&Y>a?TsD=A$p=89|j#?`I{3ZieO` zO8l+!85kHG_kb<$)^+`=#NgAdoA()%Lmwl`vGdMguLG!dODgvIJZUUaiL1)W?n(&~#3-BT#_Ap2zk}JV+X!co&0W`kE-)aM@ zdyXFmjn;sQ#a57eL=jp*EP?-`^S*(S8Y0Z@f%(X(=?qxJqu2JlA}Ga*iriCzjb9)9 zro{02AXqcF6kZ5X3a!~*2!ZS2`!A$cf>JJQJQgYaLdM%XJFj|ne(~)5=K(*wXbSjH zAkd7&|Kp{mo)BMv?eT!H|BKds0_B*)pxD<#m5~O!0xSj&l3yT0rbh25m>`zcx z0IK#RK&3z@SmX2mqCudM%hm%Wex9IphsXbm&in{=Gc-*cC{gif{{5f7C6ti?x&s$v z#uBiiqo73E5(9R~dmr#n-G9-apOhG0Ga0`1=!6gFb;hV9fSN|&lde5_ZS%f^DpG^b zN(_$2SX97kn}k5;Y(i7tzyG3ZK;}2ssCY1zB!U{k37|9vD(wRt`32d0IzM<|jw)&1 zfD{FgQQ#UC4N#aEfWjmLH0LMrUzF{$5(CJP3{X2G0PG@A$_EKS2UTF_DS_K%tZqi2 zPK68Tn1u%*vnxD$S(!lVW;!7QT^_xx-giNv1=>z<+(iX)8Yt*YKv**jd`u>!1qfOu z?9pp#1=0XISm?j#`u9qporaLa0!sdEAK~d7oG8A5+{+D4B;c~*8JG_$azO^XSbhUk z=stfTe;q{Me-XY6l!QUjozGunL7QVv5WyYgFQ0v}65Y}VSzm8>0A&V+0i6$;d+ZJu zK%75^I{%RSP6>7WJZKULbYxvQWIk)}do+2_cnx%MI%urFJD$U*yIjD>@^h)B4|MV! zbVHgiXvTdF17gM4d-ldLIvry%VDRjW2Q9<|??)5x z1kIVUsDKw{g63jDJ)Hzl)!%#sycRyrG5#^ZtwSC7}3)_mAP>`OUNQ|9;SI z3}EyAi?Tz^=Wn?PYIb!V?qW@sXJY7ZR^S(4^_2%T?wkeq1z3&1OlIUU%d>AG9%yL= z2lEjR@Q~+wA&?#ilp)W>5S1nIKAj34y}UKIK-s{9PJc$n&TtmbPInH^?lSPICVtQEI>>6Z|DxMKgSDMMzzd-rW5Bbl z$gX?-0ThnNKD`PSfvxX=buhpyzb!nv-2{%Ys4#;vE~uNq-vU~A;o12OF*yM0p!bS8 zoc;gb@tB(=qhptwgbh=v!^^Ww3@B@jo;&`(>e+e6r}Gu8e(f*TjK1=_#P5&O(*{S|Nrt2D0M<+Fy_AIdBIr@8ghJP!(Y-5>H}+lH&L^w z__qG%Z@mUOt_E~fJ#yqLfIPw;%*J}Q#kAu2MR0iZMw z=lQ4zbcU!1A7fDgWdMQh5EWt21VyKh3L_{@a)6ff>iTx43HWOM;cuD<%5lftz$5S_ zVxHZ0o{ax}I{$%ZYWdzOG5B_-33P!r7x<`HbTESAhtZ?ckr6a4(&@;_zuiTJH?7l= z7kpxxBfm%U5f7wBm`AVe9x-skNcp-F19Y5Z#aktY*PP&r29)plxsd~>`YkB0dGy*Q zNr3eoz5?wm{1+_%>AQ#=6MhiIorhmbdUPI!qz=%DT5JzNY3BJ0xrd+%>iG+{DlX<1%|7H4I_UG z=qv#nCjJ)CaxM?hMmkWItoO9M?#b_T9h{HB9sT#9fvV;o|4Z^bK@EG*wAp`8#p9g^ z8y|w${M#-V9(bJ(+8Qb0*x?4=huIyX0&2l?I(CNfcy{`Mn%SUY;dLCyxsY`#u8^fu z&%n!6YF>g;N*xEZIsV$r@Z@W?|DtQ3DKT^l7@lnY#aSZmaqy7>cx=zJ^Sa?BPtNO} zoaa3{4}yHe;MvLN+gS%Xg;xQdd_dj$IuURx_zOw}knUgBYfu9DFUka_!8H%Kjp796 z|5q(~j@$$Q)&HPAvH|}#(5C(pKk!&(3+M!@E_cQbP#MxI>JCn#F)9)}LHnD)rhNeo zvGt11y#gAw0v`)@|G((;r=Yxd{J-eD7fK8--5D7ey4_hoJAplVMGs1X92%p-0UnMA zo3R6wdwWgaiZX#_>Wi*``m(mGw}Flc@KIp_@1+6{USIdC$N%xJ1hR9HZZ{v<$)?;~@;bKOpoU~9CmC^75;Ei(W&~$fK9lk%x(4C#aZvxfIlK0G+@A-#O?2 zYVS24sQ_(5deB|Y(pjRyRGeCP^s;^f zo&5klf1sE3)Fn_NaZ&LAHS+_&qo2oJR3bnXCb)V5>B#`e6+jy1B`O}^aCr3$7DJ$w zL;Nj?U`N)d2!J~$|3!tKDlxqD0SWf9I)l6kx<2eBsABNwWmNzPfbI=@32Kje^s>5e z!#ukKRNM8kz7b<$*a=p#93pTIBmla~>?P>b4Ub;d^Z2*M=$FKQ6`3+ptTpT%{+Qp zZ-RKBi(OuWrlk*pc%ZXWUrTxPvd#nXz-ID*hMqw@&~0jtRIFZI)N8er$L6_f6+7(l;}X~ z^F6xRe7fB@JUWj#{tuAw=)8e6ABG{XfK?uRwpHh`|A871Y4{pR4E+WW1+exWD771a z_a3l;%mD556Yvt?7YG9HN>l*tN)&($K0opRnQ!3%%}JmsJ5c+l^T#oEW)KHdc`!iN zBYQNzvGC}XRR`4zpk8$ysI=>h0i9jKY6TJm9W2(e36$7c5Ae6HWn^I34_dkh9k`VT zjmLo+K=(l^Iwd@MO{aiU{qYw)S_}-1oyYbwut0n5|3yDOgjQ+C!OflHpe6$g=yU;* zZWa~LYDdI?vqq{giGxnP^HjB$gm%jtaoUvx7_W#@Zveehp26+G;E-vhMT zqY_+dzyB{9@K6bK$b|r?KLv7x2*?qj!zVSmeN?nT=fZXRs3;>`Bhl%jB8@y?tq!sS zc7Xg38dN;+nZMpuBkj1W1p~-#j!suA zkkveCovwDrTpd7S3f(>`%AKx`oxU!|Ts=S{65T#3(w(lJoxVPkS&S!=+yA&WNml~>ed^+ea-FB4Vuz*Wnioe^60g7cnTWzumVL|XO4=2 zN9)@XJ`eD=PYZB?8w;vvJ%mB^m#ED-`1qCLeNaV<9PjrZgGBhb`MHtf_as;pIo3eq z?EKuwA-5E)2AanVUK~3Q$zyWUKzR%;x;W3MQ2>oW zYk_lPXgR>&avO9nV~vUcGk>c%C?|pLgM_BjtM|Y$$ltOWoHvembh9c5F)@G^0keB_ zv+{!3sJ(|pFfD12EaB6skj9_SD$K&f@R`3JwX<9XQwtgZ_5fv6UhmVO@)F!(1~p*~ zx0DMWLZaMT3FA6?F9UYX!$+ zY|Nkru?=&1z711JHmDs5Qvo{2925)9`$3`22%7u-;n}<&6eSG&Eue9dV~!S#FB`#C zl8cJKXa0PK&yM`{YzRY2(_io10V=z}*~zC95qYnDJ-TC5B*42`aPM`w1seAPpSB^v zz`y`r*!y4fC|D5gla>h3N(aycv&el=8|MS+^b&Iaz_at%>*tX1d8F}QP#N8= z=h0lt!NA|T5VUfp^E5aI{P*cC&--={ZMprO`)p+p_jL*W4RP%_?wdng>B z9tr3MriNM#CjOQ-CeTtw1JF`N{uWSx`*c3v2daQQI&b|Kb-52th$Y!Rnh(GuvD2N?u``^26#$om&+7yti%t>^(760ifUsDAPF z0wlljf+RRV6Y>h6S_gcj&dGxyP4{0+><8sV#NYtD{(-i^K7p3jg7fiqkTu}AEU5t% zK?0z3A_1D^iUrMxDS*re&xwF`L^%Er)97+h0d2o$v0*B;_5s~E2i^>ed#kb9P2@-c zkCX`C1eM!2d^$fNb(DH-g*iY0$@}*ZIK1VKK*C#LALvxJ|Dwn4fWinI4)&m>e=pvi z2Zh7`7pxcl|9{O43Igyl@w7vbAn5Bu4T7`@j$y$bjc-7G2-vDGfp7(W%_)!*1vPzC zz()syIsy*dpk*g5kTaBzLoSd2bsbSAnwxikmg_O{w}SG&NAC>KnmdnXYX<%n5%7E{ zs6PpERVRxIXsSPfUjt-=2FyCNb2vc9K6gHYZa~^~Q;Fe&N3%9#DR^1XXV7MR$jVg~ zl?EFX7XDT)uoaU)9nsFipss}j5q3aUtbpcm9H15j!Yu+Xm~(j1arXaz(56$Cat<3N z{?_&2EDY-Wg6ad@6_X=qL=0XhfkzDVt}8J#Kx||#DR~LL^5!@*Xix{XlQj#p0htJA zgMy|vL`C3#fQ3ghGb3oG$pO?)0Ea}&H_-5?254;tWI+$i&Qe2<#v`D}j&_VY>~S2_ zd1ZK+_V54y7cAhZ0MLXYf2;Su|NoD%sIa_T4>ks31jumEiQb?Z9~`zGy{!2=K*! zskr`ObCE69;0Dh98WCLr$*cn{hQ(RmkC!Wj6r zKH+Zx*BhPh!D|YB`E))6ZxR4qL|Gc)(fOKxTaJnXctb%gCC6KzX13kQPAq7*WiQB1dcHZ`lj-3wXhN0~@HT<-o%)z?unWqAZy2hG;0M^XNPZnLQHx z$p~6tk1~6d3RM*W>M`~5+8qEDs^G>jXfPep?tl%ZLwJzJFlgXR0MQ`s^keJ{WAW&8 z<9KNSo}PI2UzF)OC_y4O6*pW1iy--G9#{laz(K~h_JSh*{tNL=P_hLH!zwb@Qy`i5 zFJi&;{TE+5Kq}DZn=C*l6-j`%Y*>S0r4yXK_JYa_&=wS*Zr!gJLG^;^wN3y3zm~<= zf8zN68Ym81KocCEmTx>dUwQI7UD*%HY(AavEhsXW^FM}{UW-H8tN%q$T>wq_fCs5? z?LUEDHq?5lB%tMXNd;)4ClfT00GsH^2T$}Ag4^KWK?rLP^ut*ATfpaig0eLvi5i18 z!^ks$W{+aPN%I6OX`Y8AO-)eJRE6$Une&?Sg?ubHMNR~zNYFG7C{`u-1=&C;vP4DV z7#sN3-R2|6Z5-r8seTcfD35{0`8$vE3$VUsh9}A^U?xhU{B{AVp&62jeL5}pHCQXY zVJ6Ho5S1mdkY3u-y(kG2GOY?~X3|>B`;L&U9xewH6fp(h}{)-;G2gM_)NfS+IRw;; zL{tg?MS0IFfes+<1>K+`zyMp4(tIQUJdkr3*~>}4Q}Y}+X@jb0}F%74^aF<=KnQ3Ixm7&Ux3abGY55;Bwqag@&A8AjfwReR7$8Laoj|JevE%-nZpjq7iqKfB0 zoigO(r~Zrbo>Ky^1$E>XWZ%FDTk!>23;{lm%0>meJ^KY4*kjcKFJzDY{|{O_3Q`Z6 zD_PqN@*kp&51Ic1A6|&XKmEU`>7PK*@DOM#GQ7?AU)1(2QT{Ol4IF?@*z!Gs>Yuw! zX#PQJe>A=UZR>-!I66Un_ueu=P}4)vr*{c>37Jo??i>!#DqZF-X8{|w5}((4pr)k& zsMW2&uK{XpYs9EH@C!y8@Cybj@C$ki@C!2Y3o!F<1FM(u?DmrfjRoX8@(b`e@(Y9s zfHrv~fUXu-m38IYx*N{^#d5EOy}-U9pr!3z8W(FU)jjyYH`y;epO1}o+l zj4P;JQ|OHA{D98ghJD|lLmDe$+pgF>OZTEertOv1zRdkNQn(S|ch46O%B3p~1m4Lq&G z3`)~Hx~nxjyUR2-GlG;v{uhmfDDi#m+vP0iXyqozKjrXiN0_OlrXC0rOQkn6f^J8L zSXe5BBvtbCzo;!d106vM+r^{9b z0X)8>EoOMh)ABHXYs$a>|G^OlR{;I&Uao!?;T z*`rs48+?&T8viyiP#X4VKKQ@kmwxFp{wW7o82Pt-Xnw}%p?MCxc*Dc;Gk+5(WrL>d zTknC=5aV|b%isJ>uAp`g<3F$;HGhK!oj6LTyxhfrd{!tp{5(4E8U6?D_&oX&t>=cy>OAg&+8wh<`6Ymui5v4|=ecDuPdCQ~m2q-l{T1?>8=T zOF#NBUhvU8;L&R%;L&`X(Zli|f74k|1TdcP>1AQ^Y(Bu~2u^XJW5YC$c`|+kMSZ~z z@UoWXe@rEA;8;rW=;b~5osq%Q@-=n}Fd-8)p7V2W{BEn@RXx4;r3a z0lHtxhRLJX$CGIXXyJwjzw5!*DjvPOQ=s~J9Qn6%H2&1nV_+!d*#R0cdd&(}>D$Q< z8Wr~KbQkdKESCT^201|UXA+M5g6;~A{DS@lpc+oYv(p{4O$2hv#bNNqi|xljkpND= z;PwnCKtZ?6yL8oAx^%@^xmw)kZ-2MpP9eqI+*pFnZM;Mh{eC{yiYGH_Zv{j z&eP$;c<}$jV{VpA4Rsa_rIlTN0*>36K>4_dpP7N70n`v+12fo43q88!K$Gkqz155! zoxea^Qutji^S3+$Pk!idcS(C#zA6!D_^nsU-C(89-wIl;`C7=s@)dvUAtnZfhTnSp zt)P+tl&3v9?L0dDI6RJnc62bj)&(2fda^{+qnV8nq5v!ln%R2I56(}ZmDqd0=?HX` zKRA7Yr)Dp_2d%CV4cw^206r?_#c@;cwwc~A0hcZ#!;_8;|JXeET~C$r9CNc|bllGP z5_H%G|29dFUQvNHp2A1irRPgA{XY^uR#8QyE9P01I@->8!eCK;~burKZ-5Dw{7hQ zRf!<3=1b81@7QdH=wSkRw@L8-|Nr26+@n{QA8c97Tm}!u*Z&`bbv7Sj@##G1p?Q(N z3ADn-hw+q0rx-YbyZsD6O<;L`0XIQ@0Y7jpBmk;~z?;>;Ebw(m;D)k4^r8@-P7e-` z&I$pSjsgi#C1e0<7d!9^I!N*hdT4^Cy!<^nJuF^+{`>zwxNe77UfllL5A2+7PX&+O zOh!l&eF+V2h#LOpc+j|q8cuagFm>jjI==I|4=9pdj({Q=oUu9>VHv9q#Nyv}-ZL4R zk9@kzIKV61LCq@AJbdRzkMB1g7O!`>pY*%j~-xMrKk3S%B9z!GVLv>iEDYfgtft1uhg~6PXN?p zM60n4FMT`6$Uo&!!%tAD7Q_VB?ZNN*^0gA=u)5A$o}DLOi|z#%Wqgq06ea_T7zI#l z2Y`1r3izl9fEpd((=ouQ0I?tk0{TH2c2oz6f^%LWJ zpU!XpMGx!;$)VLBu>BK|^YC2{Auo;u-(MZ&1u9Hn^((l%g)NBH@aSg$#=v-tfq}mT zyk@Rf_77+T9<*f0`;ZcY$8pvhhm{yWz~eaURTvW_eP%zX^De6jnviO}U1ACv;RmfC z0-aeX02*Kf2}m#~fR--SsBnOazKsW!81{ihKn6G;P-55(DjXdllhnPsD?xKy-L{tw zDlxncg}E1WF0yCmJJ5`q1gP)G@uF+l|NlPSwyQy=!F$@^y$%=lDlx!Kiw3#B`8OB; z^aCy&FZf&MFfcH<8ou>t{>|ygKmDjj^KUMX-e^Woe&>50o$nq0U-#)&^#{42Q`ZBe z6*S-ae4i2nX!G56&*ndjB{`nmIx67R>Av78zJt$Me0xpwK`Y(C_h$)!SP~$YDj?zr zw93524wMl=L%*&6`CGKWoKg<3&zg^acrkO!|NpPA!^S#bN7?k+-unQq7P!_ZG3=8E zogJ}39~6@wy|$h&!7>%Am0(M*O%H&Q256mu5NQ0T6EwjL9^r{u3QAbdUwqjNqQAfB zDMB4kKuHha3pgQzxt^VWJi7Hkturpr`g?UyiQQ|vZy#tv19YxZ=WCDVzf484uk8?i z1&tqp!pH+6S*+~Qc>|oCKX~$QCGZ#KgeRd87FOGkCG5 z*h@tw*nUR1Xz5b4iAL~Q4W-^5&~qA^ML>%G3Fo3eMfJf(z55D{x zO;k!uLCr8wZymIWb}8hHfq6ci?_Y?4#?Pm`_`e%8@CiCtpmhDq?VvrC6CD3v2hFia zv>Yh;0&32HZ*^-C`~UwxSUZ0!H+X*F{fo;R|Nnpa57b+_?bB;JbuTQSKnFm50X5;8 z|1$HpSb|E!=D&>mEj5e`44p4vUKW3;2kPL;BZT-{L48X6e(o0r`S~SQKfi$b*%35f z0`v1iu%k<7f&8qr@&EtV6JBs5`x&%`9T7R8_{8S_5-Er=!XRUAZ}|Top=AO%{y_&x z@qk+V2T-G*zxC}uP%=zW;Q$p6pmVVp48M6a|7LXHpK=T|)&jnRP6MIKshwif9dw^g>wyw|P+Au_<|x4E)A=7Xod~*-4iY;@L473K z_5c6B-tEzRqyXI91T6&Qc?u4**~{UD$B*5R009ltLKivrf)sUvW_Ei)r#f|lhS+*p z^=E>74nCv8N5un_xxlwWXL$6o&RGZ=LkGDEbQlLX;*PthfUcDWANv#lawp1EFY|K9 ztbo9OQHJec`ys(D@IrDC$d>yr9_AtiyTFT2b0NZ~z{0Tl0j2!XfOX11lMS6EDgr*e zs=<3e3%;KJ7p>Y2VyLKibjzlL5>xB#5?fHxE6woewbk9D#PDDA`wmd0mZKs7T9FaT z;L%+Q9<$~LrA!Ww?ofgMqB+}@7z|Io1a-=g2jkyvgHBky0j0Xm!yd;$NA*IFP~hoRy#}Wk<9Bm6C(y@?*ZEM`8I4}AFDAAr}fyBfX)&3t`- z2wI9d@c@4VXe`gM;M1$x^MH{7 zv^gBSr4Dp52INX3$gsqJQPu5`!D7&m5dXHzKE14yLB{p&QGpzz;-g{#ZmOkj0j2Ia z5K&Nbhh+yyV`(nPF7V>@V=gM};H<$0${HM?Da!~DO8_+3BLU)q57kuw?ZXExzqvdg z;%rcAogM^gR+XqE{1;W(ti;ezqr%Pr3J-`Y{)?{P0sQaUTad6uF^Bg^i)q&ZC$0{$hxo*n+AvMFkN?paFf* z-7o(|S+;?!E^P&684J+50#VTL4cKDnx#pspTa-Y@N49|yL2r$U#fu-%4F4e$lmZZy z38?<@XnrFBKI4?|RXB}opz|M(NFZ%MMmaB{@y!qLcsb}4Q}DGd;9FW7EExD(%|UlX z_U-|%ZSv{-_h0lYDE^xFfDgD~;cpRV0nMVcsJsAetkeWea|l2dLV@NgK!cs2xeCzI zK+s$TXhwry6Ev@aey>oCiok!-OB=y?l)vQ=_^>$M#m_m1IE%kpY9ly3{cZU!>8BbzYqB8T+oHAKHWZ`m6_nV3?2*6h=V71KYa~oG{f>D ze{&Ku1A|YejS6T%S^#K!r3EM|JV3$T3EJ_b`Prj$4tSlHZ|76b?{{B*PM|0%!BcSOXo?~mXnVBQx5ucP61D%ctG!01sx3p&Ig^4BMd;zEHMwu zLnVUzZTifhUZ)Mye*QicW(J0R3=Dz{ASpi2UK=Lz|i;) zq=3H#w9OSf;srTeoxiCXbW$9sgVFd8RK)T(W&QvEAEcU*0kXTR`6k25Fi;!2x17<( z@>q!j|F&{QkLKe{&Cl#RUpGINcWC%~zs$RL5 z{ZQlxlJw)>X5akW{(mZ!`oiHJnSBzhy&$D zNB(U*T%P=k&lf-PwY=lgdDD~M{ftMi2|sf70PX*eW(pZi(Uu#bUp*OFFY+j^EY#Y+|YdNKWGHA`6!6f z%VP(2$|=YQfsCP}l?+$OB@brMG-Ed;)j&<-Zwdf6fn!uMUOofWejqP^7?89FDucUo zKzAILfSL>?DjdGODq6n1I+~uH|6#s7U8LyAc-*J+Kg`T#P*tUZANL&@uu?{%tY53?Pp?TFKaW^wu+$TzkFQqx0PaP>JW{0rT5S1_lN% znBU%k&OqVc2KC!DACTW(R)A6t<8jc!n+Q-@4Z1s%;WY=?bu}sxo}j(o42-Xx!3uo3 zK@-w-68}X_*Fj230nj~^5}w^|3Lf3y6Cwn_yC?HDC^2|;`)PE5R~`%e7fl7JZ#)7j zkU-@wqSJS0I;bJ?ZzHIV1ueG&&pUQP3L6j~lD#0SFF}0JNH-|kfeInebSh|h-i!2k zpn}w+*LK--kh2c`7hMZA&~^=2EKI$H9!@b0jMtH2eH82 zO9v1aylcw?6jlKqy}SxDL6v2QiUueNGyE52SPQPx_!&yL4G+LZ^A9)FsPHkA#zMA* z99;wQ7-)wW=;m5bWh3xkv;$PKf=)FB4~K(J3kH=Fpk{&uXlbVg=-}T9kIrMRh6nzO zPF@RY>v>B+qD11q=p~SmpxdFE85ljfoh3jO!xV(|pkfoesB0Iv5q$rJQ!*%|{-;SG z0tX9E zB>}YQr@*6^^E5H92)m^K^ z@Y>F&yN<)7I}RNB9vE@g;_?sFxdj~w^TpqU;@@7Q;*r)_qvFsBI>MktMFdm=NVs(7s3;tBQBh&-a1~GMtQ7?rA_yLoyJpM4 zAjrVq3OYyuwCMrV4C3&xJjma21(Y&+3mH8+O;q@|3AS9S5OwUl`VusckFXpxUTt}- z#NVU0k_mLDwhRBZOh(7%r}mu}n;*(I{JLM};$eBb#LJ_%9BL4##&XM;eqCd><1qTK@30Z$a&NebU%wALuo9Edz))iG+6jsTtHbJbTXXde`pxA zfCkciI{$+P^S7@8HM03z!$CXLeN+URf3onmo(3I#aUE@jodG-m3j!DW@=znFPcv<^6$OS{7L5EE2id8>^ng{|Ce0<{{P=s1TvKY-94b}<>1+T zgb`E?fER#!c=WP{b%8PfWRPJNxQ2peP0#?bM=$HLJD{^FVWSHWLD1j?Xiq1omIj|C z4L;!P*?-Z-m7wzD@c)(rrIsG8w@Z5vY29xHsL+R`^;QMY4amKw_BW98QOYVMhR%bp znV>!540vvhg`95$S`pW4>vxBdVV@-Ecu`gD<;WxM;QbWMZ@^cq^m>5iZ}|H_r6?%N zHrI17d33UuR)Sm%+NTb_Uq#@*XvA{J=}`i32~b~E0#qz(fHzOHf>t&9^cHh?KsO6C zS95@G;d54ijP-DO^j0gp2$=i-KWG|O05lB?nt}BLt&m~^b^V(UFnV+{gN_UHK`|rM zqZ@1n17lq&XdeOOc#k;HI1&FPP``xt@C48bW7Z}8kUE+JyjtLzfjszMR28Jm#iQ4D8rU?^vs2-tPE9M77+!0FHk*3%+NMHGxz-Q1OdidY z`zt}sA<)1!Xm7xaihfWz`~Ag%Xi#wp5(b@@*b5O{4HNX~{QhDdGG`)~gPhwy_Z5QI zpESP-@C4lv1a9MSAP>4i_M1Y+nk77X8&-hYhus{Yrlv>h$$I6Npo;Frv8Dh2gUe4f zP`-sr^Y?86<+tVv4krHAGazQSGq~xa09p#B=>i&PDghlQ2HGq=G$-=zxw|&~Q1vR+Va_6QpH0V|XkUMG3<+P2&TDH0W!qQtOI z4K#qtC(ZylYj^4?kPI&yM7uv!J0wjAg0w4p^xB$V1}jp8C~^WtdgmkP&Zak@Ji-0J zvssU^6ub>b<3%B;iP~$+2+{Sr7wj?~6kXfEx}Z%pgBSkb8vp)_>5-s>0u>f`aibqp z+T4H9fGo`MVqzymI1?-k+wTEdn~(v@zTM><$5}u(*D!#aF0+>^f$pzhX+2OH-mRn3 z`nFUbw1)>-Nd-cQNwtRrl4c<%B3mvBg6`Z!sAUA^xPSDZKojxkyK_t+C5myh$ zI8YNAG!P3amqEn~__S3}*$X~+807#tLr}pBYU2JE&07e{a2~y^zLSs*0T;ZW`X1C! z^5|uaKMN_AA;v%iL930Qvw=q8!Tn40_6z7pF^_IrTaaH{|Cd&Kbemc(Q({04HpeBf zkmYYJW@2FImQiWFU25jhZOV_PQXEO8CqyMEkbf*uVgNOvFMwv1?n46E_ViLE253Ov z01erJ2Ic-Q0yjTPL_NA~S1$#z|MR!Hfo@*|Z2&+X+T;ZdZ-W+mW`P<=7~{L$wmA^( zU=L+1MfFg@VuXhlfvRk9asheB4o#&Sl1k83C(u-*4)V}j{+9osIU{JQvHi6K;-RDe zMcKg~0__|6FIv0+RK?le0SQ3^8025%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( zPMA%n#39LXTLwyUEWl~gGN?_Tz&7>4Y$`<91R8x~?gQEMsWy$dk4caDQ!P9NUY3NU zz}*={roh#p6v)cJ{1&uQ1D4T2u?X@R2OINbCTKD|APz}}2QpBT;R;+~wgGDLi&`cn za5WFg;m9qE8BCx=_o6n9c?K-Fc8fz2-O&t`MAv}RlC4ln9%EPnDkwPEnCF5md0d;u zJeNt2`Ee~g(ZNFMR0hFBrvUCj2(U5FX7&P)bVPs$fNY>_edhIyAoj(&32Y@zkQDY% z0@6nZjr8%bvNP{4Ph(>aOk>sub>~djn7^=qP6ifW-p^9PoB_Hb@>C_L5phjy4J!|` zgdPhIs|<5fI%sfAlzB3*7pw4W79Lh(=8r|7)*%n`UkNW39#%o-=5#MsS#R*s!GEB- zTA;cnf^@lqb$y2F`VH09iqQ27s;ey>bb_TQb3a1YSE#O^P+jd{B?8P3CA?Tzm{(;& zT=GtA0t+XrAoGL_kc)(w=hT;gdVS1?*dhFV72vx)FV#UCCZ{+fSU>^tt{imga2hKE z^NBiW+5VpqbRn89a~EVFkNGWo6AKS32lD~wpyWy{gOWU~e9U|5^;o&V?VF=@pdk|h zHs&eJO{}`iUu!`lb=^!6;O;V{oqw4bTm~@T6#xzE=rH$WK!%;B*Msvks4>IB1F8@j z>!7_h&_EE#8Ar-N{bL>GrC6K+8q}T328uQg=0z2d@uhq0AU+TCV$i{jj&MJMJ7UZ* z#cM&5+Yucfo8*s3_F9-D) z1eiAogFV6w8nfkL>vx8!bgZXPYC@BgsuMtiIwc)OT zTdB;gj38Ma=A<+x&}1LZk#OiJ95@3(h6+KqU;ScA181NTR!{~4u|XNg2JC3mQ94k1 z>8&hbWBy;8#@xsRiUt!l=0(iVt`#f`?W_W2q35EzSa?`%ncrl9{KUh&MjR9ka?DpU zKwc1KKFAHtO}fm7OCV`@wKyoP@-bh{@M4vRi7S-GJ)ai_mo(s%tq^*Ub!2h7(}!7x!Y}WL0Bs&w#jPqJ#|_$Td=I%rBuzUWj?I ziZUN5v0)WXW))xq#jXh(^W73JR(>sT)}Jcw1=b_R#{3?t=cgDXlY;!r!~9N+17r&* zmx6Q)Gj}mUa_JS;G!`CKVdgbe;1w=UYM`NiiW4Nx&U~j5Gz^%=e5(eOu^?lSY|IPm znm`j0os5t&?k)Q~aDtmuX~V|+qZXkOG!w!M%2=RWx|yj2noF;P4Fl)WdEi_M890Ux zYJp3S%gmtA5J1YMpx&A!)G!7%<}OGv0~)3U`G|vg8p!f5*enO7<7Mn6U?+a5gp>k3 zY@kHJ!~7Mb>wPUagMbEYARhS-5`9_=&$7@=3A(@$oTm>=mFDuAM4(2{6 z{}|W`4(5ZE^FUPs^8-P!E#OgGP&RC-f)qge>%BlB3N0Ry$74Ybf6Wex2oC1spz48D zfccgnI0HUG&461NoABpA&=LahSRH8m7&Z&lmY>GL%gVyMtvro|lhuIvG`Q&}!n{=y zG9o`U>l!#Rb}~n>@Un6mvc|^T$q0&d5$3s4kPzRQ zhKO~@Y!%Wl0k%dpj*1#qX@klc%!(RPAcJND_*mJQw}UF`P(5aCXhr>lrHNI5`C~08 zV}LReqP7DCD$}8MUb*4Lmh6nI(;d4-`^ItGr-MTu=z1R+W6L z9L#&bQKZAX7E2uka_?z&J+NzbgKJ-q89Hptw-`Wz;L&(!f9F4A1jIs=N)Tkh0ca(N ztu6+oS5UnMQa2A;2|fW^$iqCh0+QO7z#Rs!+CYs>aQb0mekBRAmz#NcI%w+3hJ}+= znvJ=K5p;*aE;fCLlOBQdxCtBcVpec)p@bI5318Sip~b>Hp%N5@y3DbV)C`(mf(9bE zrU$h~!2^Wgx)|i<6&2u#AW(CH2RzdYo-&<K~2OZbb4O9~=vN3lv>aj}iW;J6=W0g*06|rHn1*Il7=0~-l zoB~eYkctJ#Ek>Ynl9&Wt!U77N6_7b9c!CBQtiy~*&`>w>fx52e8BTx;`?KH-3s1h_ zG!1D}f=ql}4vAZ6@_m9W`GWk)!Tb@b5}JIGDlBmFZ9*hpg_TG>NPbp+F6O>E8&)1R zFIIlG7*-y(H0DAtR(@vwCRUy_=0ecf;r#Phc|h4niH&&z8#s4QXW?h%XMS4g#md8` z$6N|3F!ESAn0difIP)t;Jy0Q|z`_smsVJo6dBs?Q7*d^p#SI{{KyJvw?go%^SXfyp zbVC!u4VV?$&pJjH0agy?N%bX&0T|rMro)smBdkY9AMe++i$?pt| ztRh?@tWvhDBE_sy2CO2Wp-ooaAS?#`V*roka4}C3W(4&Rzy%v<;s8A1%*q?Z#LNI% z8iUnkkys3by9}Eo+-2UZB6Hz$5v;rkSPVjU892=%xhw<6NF^()EEjXXlnsj@s|fR- zWN@9q{GAck2pooTP*wDXBaKB6RB`u|gDx0ge#!uzk(*r)9>Xz9W7dZlJBtz5941y{ zUvPk2#lif)98y9wa(J-_f(G5nA;wKWPTDBxMUYj5i@CcFqU<|k8V z87466LQH67oCgVUjA{tQgm)Yu6F8XrD!?m(n13^@!J&5+y94*|zKHpkfKc2el*xm>aVo{Qshm;(KQ{ zMEpdZ4VxFJ-^Ihmj4pSx7NT}J6R6oBz=UB2keP{7SJ*(@QB~m3S@^v6AUwGuv!Q-r@0ieK#KVndm8v0^^S@( zR(^eOZSkEQbk;jKW1Kh>W1kD|HYhPa zV+9>E&%nm~uo6CG{RC8CgF;J~jd?a(1RL|y$`VjnJD&}%wuwa;bjy)a364YzbvvY5 zL%7`r_YSMHE5hM zDKX-RHJGO%o$SSR9H5KOIG8&sCa^Kb=&|y%F{^@;5i=tC(%6{8z%tt4_IoIJzWfsd zhY+Z*#N5lc2DFxqnZy79r45V#0NwNk3V<~z0RRdf=Ih|=hPui*gkT{7YN22W2~hZ6 zX95QV^O|ywCY<3Q!o@s8bOIam>x?E)QP?CkfmM@vS}g};ZOFYsXu4>E7OL%xCqRMA zytwWJP9p`ln4hwPj=|r|TLLl|lr+F=yC8$ntJw8eHJP{Zg4)LV%nPzXMUW17I2P3V zwgp`_$IHC9PJtNn=Yh-zbuhu^a|nUQKvuEafGq%(ejp1XplK0Oo7mbwJ%D6E0%|cT zz{T9g1WU)DHvTI1G*%Jj&Ae$WBA{gdIs>%C6Ko~8Q^O3B2YCrprh{w*b*MpJf>cDd zpew;ZUcxn92~LfmAQNVO!CS&Az^2E%4K!R2T8+Ac2{gDSzzhmCCFZx#PunyWX zLAD2!mtWW6>Ue-HSOWKuG1x<#pvA+R89|olRIv&$FJS^Vf0*xpCs?Y|K=VON;I>{xO>h7_6GAGaMxIXxrIrA4V)xF z)4IqZ1x*y-Gy(R=ez->1Z-Hx(Kec)YA0%M-0BR4j3)lyC zNIqDI%LkAk`U_d@3?7U^SOE10Byod1(pv`_I>1a~=&{WLim55g5p2wFYMNL?SvA?1 z-!L?>a=^lCG9#|0H`p4`KnMpL$Qnq*v#=sZ0$W5H^WSPv5|(FU{(@|5H-2OHW4K-i zbg3*fZrLJ0VZr>j+J;r0jrj+|350XoahfXz%6N<-ofi5Il#t0gT(__9> z2daAo*qA4Zq_L&#Vq?C^x{F1Om7Do+I;d^n#mdc=4IYSLVr5`HSEmPE-U4c|DX@rv z?p@u%JQv@t9K;xtppz$L}iU6s#0jCHM3z{|&5nf`; zDp(3n5m*8PbV+W77brz|v2r-D@-y>Au!=)02G=5Tgp&sc7jq6H6FX=O2BWVo2DF*Ch$S_AY&Gw%8GG$v2vUQ8^SgZ$A}dts8bUI zPHfEDkluSJDAGBYwM#(7Ik+VX%DEA25lyh1%fUP=LyzqmD+BYNS`O$;*(q^7R;6rE zqGjHiRl){ccd<#9LkKK(F6jiwxD(7ROc9{soQ*kp0;~!KCv;VCtT8VV0!11J^SxZ~ zDsXdfX520d%8VS$P(vnw3<0;q*q9dyO#qt@S1-oO;SU-IWIinlvPhPBLRJY_g(@hI zXfjU`wP9m^kpU`zIM_f2fozO!{3M1H|voa>I&4Z@OXX0z% zVqT!z$OYJ#pNVsTiaJmvOknex5CLu*u_dyIf!xwq2lA!}8}n3gJyuOah~SG1_(T$T z!4JZLpaf=);u^45%pv+3>%0(siwmfUi-U`qpTmYNg854=)Lo#^c>)TY6JRGVf+mqK zwH(l`W~an$U`b?eRuda&+EIXwd6n!nu=;CBpV+`F2-uis3C&}kz|8?F{5hCG;uD}& z%o3UhR{Ja$Y^DhIhWY|LM3 zL0Z7IQUn`^0*b>yA^D{?f|Y~0hlyhilc^B%m)cLvJxpNVGWWoiN8J-=vVg?dq6`iN zHZQQF1lXAGiF3fh04%%)l*YiqkfaDv{e+_lv`OhpEhr$sF=oT6X$W%uCy*!EK=wf# zVgq&vbO@oC1C&F+%jup#VuurX6n8QsBP%bL2qe3s$Sh+7EdbYNn0&6qC zBJ-F5Vjvr+3_{WK94Z4IN=A|S4IX}$BS<CVJ_y`tc;+6Jug-T=Gkm| ztioxmN^BM2=zCmcLpUxjfwZrx1Zls9s(npW1kUJa2S=p<7jvgDBmjTr%!5>nC|-so zYs?scBx_q%-rHCbFCVii%z8r0zeC2QzJ0g3_W$vOg(tRY%nWrD(j zsAO#e8WDgbYlzE~tl&-l15CRAMWD2E166 z*o?uNU_~`HO-N}PvyOpyZULnAi((hVa|<9-oG3C#X&U0WCFRgLHxy-9(liIQWQZkE zV>1p*qQ+_*Jm|M`LtJ;dgh?GbFpuIsNZ^ChwF0QbZUU$4d5{wO7C2ZRSAl9k0&#JP zRe_CpRV7IKH8vFOYrxv)V2w&F>6+sSmeh9<+@!!(_Ff0KL~P-6;3%HE3zigSm1WZd z9S+6IW=pc!yj<991sTlCg)N_aVPs@A<>F)IHNj#PNYVi!iJ1sNl3<-q_;fD8AqjFP zb~7EgELeG~@MvSiXpQwVF|x{W3A5U=fjSgCY|J0QjUmt?Q!iHDQY>aoVPa&};nD{+ zpxA6#d9hU-Gnp7!g}H=5!{DsaYzC}~b6KVJSw+~qSY_BESw+CBydTx-fhPV|FzK-| z->6k!m0|-O@xUt1X2Z(B{IFJ!RgsN(0h0}@NF=B=d%xBOsv3)n`k6rOIvy_OPvVdq zFfk(yOJe}xa2qynR$gog1li$=Z2GLy-mHqLtkSluB5V)t za|kj2VF0Btj)_d_!k}Eo!{N=!;Rz1qW<1f3VhYFr=D!Ra^H4KeGycqma0OgHYDObf zKa)2jb_ak9BM}A$hX4Qn|7T&~U|_5e;9=*O06GVbK?N+!z@P-(;cW@!JMsy%F*)J9z*H(&H>UVi$ix_idOIBI^}wcKnqvnw#|A17qvk=yo%jTt_#~Y8 z6dd_9npt~T`k4Efm}{B%6u_baPJA4Wd>UBX?FTgo-9K75#Br&Q#-ZK}hd3_vYjLO# z#v#r|KpYmIupRqy&~RsJ;1g(OVsZsXCIbUJ-W#AB>6KyX8d24uhtqT%_B-GZC*)q3 z`LIplztGHq?a}^+CJyB?#6cM_bD&&?94G@O4%=)z1x*~vWk`iGVCtbq`Na_w zt<18FpnM?^4JOmTWE_}`0+aq=(icn?fk_7R{I?!z9@hLPOE~{cg*pt~oj;)I5A05+ z?O@9x&I|{$k)6rp2o{34*%QphaHkB^osKBwL?25(a~l(LCnKz!0GotdPQX?^r$F5a z3uoABWL)Wl2TeVc%YaLq6{-McK9tLVOI#AF0A0NT4sl}h9aKAmI+THKzA6rJUL4}M z%28T#K)i$lF44)F|hap+>32y}5MzZXiQ`*$5wJOoO^s4_(Q1lKSMPJ9|pd1dV{x(K42exts z=3aDrHPO_=)~P6=i9@*zwonGTIk0vay0|$G^{;V=|HdKC15MxP=Cec9%Rp%uB@Sgc z@(F~3+7vK-u*qpqn-Qc0rVlnP4U2adK7|BieIT_U3_X&C0hXR&Gmdr4AX#v_g6BVI zdWPgX<}7BU1P#t|paczb7eqG$&hiykIRv#9qdkkOe8r_679QyC;fFd5U0e`{I4<*X zxd)dx3l8&fsmE0>!O{o1`*HafS3Mw$!#%jd0hc?4aF~P3zqs6qE1bD-n2*a|Q5@=V zna_tkWtNw?j4;T%#jscgwa!>{6>T$UrS3bfOJ_1nl zVdXZI%K(cnM?L{aYZxj)tU7e};<6uCdcjo=;S!gCx)0r6TCM&caD4l zx$sICss9KyjscfCba&xuzmb|haivRK=HL>?m7Z|rPh9bd%Y0n<7FYR&OFb_0Vd)q> zoNc7CoE0S{(^U@W3e8PMcG>Sm?94M5f78Pga=P4NKnHcLC=;mdnXO!erDrms(KTt3- zFf!0JFxE9N($r)C-PVwknN(bo0=o17boM@}_a!itl_VzRq%stjq%f2hWtODIr{$I~ z6jv6-dA_H9G;~5G{GKx|YQ{us<#g{OI<`pNVr8=kPs$zl~r+NG0YgSEml^jD(x5& zOY(CwlacK%wXkDw1dBT5=jSlE==p?%j3&ZlR+U~SenWZpPN{eom#{Ykdc^IlAr66SdwT1a(rlB zW?^Y6n3v51*IS=>=+6X^D>jOQ&Sk6 zO4GtYF<*q?9Kw#wOHECQPs=GS&Oi<*aBP5l2@5JKE1%4~RB~+r#dsPxrBK~qHL!_@ z!5Fo9wentUM%)dy4}6r~oI=9Dn_XEXR0GX!Vm7UZNd_~nFL5kO%*+9oVNON)*{OK}nFXl~jya&hrqZ<{v$&*~Aw0h{C&dX|etG7VCFW$N zc;*$9mM}m$puEHol9`*D;$K?A08XV*sYUq=o_QsyMMb3rAUF7zWTX}`gyyAI6r?7X zq^7v$r!hE}7MJAbGH8JUsMLZ%ExNQgvnthE!8n!yUM7LUEVZa8zsSl8q8yZ%3yK(m zz@cWNp{auiTc|o$ka9Z))Nlv8%E}5}L|a)orX&`W!eaCL9BhCB!XPS5s}-8HyRvo;9v&D1XN#eNlI!_ z5jZ=e$`gnwtVY-|q@`st*ZX#l;Y|Uuh160Z{{CgNx4i(t`Msdx^aD=TD$HXuo`7D(a;ag+1&QpysG7{U`XOF*SH zsHFrhMHw`}=^4`$xL#->3sC_z$I1%BAWenV^HPfvLA7OkW(tE7L^QZ0u>_@Eiew&Cqm>o1T0Fr_SSwN_5LS#Fd`PO1V-^x8 zNRrr{Y{!t8TvD2t6JK1CSX9DLlv-kXDqKn_3K^K)sLByzIUP)?234?P^eqL${LwtN$Vo_#dUWp-sX~a+r z&Q|e>DJey%#l=u=F|0%Y`vu7+C$!v%++ru77L-50B?&=w&{BqgI$R|Xv@FA^+YJ=T zD8(FprG(uLHqj0gPoUBdziGH!?VMOpQd)$W^pV0Is)SIA$EE{4p<`1+TKdLf0;uu= zH4+&Lit<5)ErVNTPO3|4aWaErUOuGh3aKlh@>W*P8JRgLkouWHO$}T!K`Q_ZCHbW# z3~EsAi6t3URso468DNnjTv{^o7}TJ(9a{SkVh*T+2bc6#RuHR8%HoStlNn0N;`2Z> zs2naXNKJ;CUy_-dYGoB%SzMBu3zCAgCZQIq7nwT(wrQI_;^siydop9w74X*ER{hGDK@|wtgHe+`rL9$ zAeMp6Wk}1+$zkwGO)Fst0*$>eIH!WDP=?UF?7aN)JO;;{%=En6)Vvagf}+&q%wkag zAKYRBH$9D7Ni!Vv>PC8D^LXs>YN}c1zQTL9-yrakoV)`jUkpo zEP{0CK)qa0#}3>@fk=S81WpEs;5THbu7*ZMZ7sxKCoYjPAib6jva%0sAG@|LwuB^PDHL1~KwWML4@=z)lm!eP?~13aEeWKI|?uC*gRrwkq^<4wqe#hJ*J@Q`pYW)3wg22Tzl zXVDa61G7{MW5Xo#G)r@HvlMU^1r4{-A&Y`b4J#|0H7#t|)s8-Ska*|NFO!ktCR!#+ z;@v^7Y(l(?u;+u+q9WvcU}<2PVv%BLXqjr1mXer~=E~rbnpB$Zk(yJGS_EsHgWGu+ zedf$OJNydrOG^l-NG&R|gZ1a*GePYJ>CrdSwRn3$WnGWa+Khs68&hciG` z!>V-*BP?Jl4b3ww%}o=N5-m*(QjATLl1+_W84^q4Q!6q{;xls#U?vzEAjWVF4UrVU z9Fv-2YHDGcVrXn+Y-nbll;p|~80s48%HWpgpO>5p9=}B`g^-%>2sKzUBX*VWF(zok z1D7i3$QWK#xJH1$rr>ZfG|wE{^|4@&je3^X)?^zuOqQWA44LBrvh z$=<1zmY^OaSidD)zoB`Csc}kLnuU>RT5^hkaZ-}0D??FgdS-D+YEgVjNq$j$T4GKP zD6kk@Li~e*8A`$HQWR1vN>YpR6qKBm6w>lE!0DpYLK7D1coo?(z@0?c`9?p zQY$NG$e^7aLo#TV3%2SBG+Ra1aw&*LJJ5Ox@U$6d+yopC$f*RRs4Nk*H~})bM$GsH zB==cafjfLeweC=LU@h|@op02E5_D~jMahs*LzRGKO0ef(QjnoJP@MsvZ^T;tV$)>D zP@I{bmza~9!jK1=8-mtjV5Jauf~t1N{2|(kEc{BbRu<;TIjM>MrHPTD ziG{Iga+*bQiba|+#8gNzTT~9JZQzUaAY8JN9ZahobU7i&+|(3gH-pB9?HKY(bK^m4 zbW)3o8G^#WOAnx~wuG!q%gjr+1i2jSKtuBkGm}J%G!r9pa|2_;6eDv(S4eoc<=JRJ zmk@yUfyO`cGLt>?((*O!7(DXxvtflW!dzI0q#7ieo0+C1rI{HTSsGg;xiTc?RWjtJ z78fU`r!o{|rY5JtXZb*5y9{d4AS#x@vzQ?uu_!T@!85lYClkDgsWh*I0W#bl44Myt zCIm>D0}q@Rl~`Go8CqF67NwVhXAsn)@yN!)QZ;^6b_|I{>BS7t007$qT0)TsS}_Qj zW&lq<#PCxa)q z8ItpJV17W4M20lD8K4+<^9%|Oi4Sn}^K{0HeM2K; zwMG{4Dy8O0DqsTI7*{DZPXP&}s2Ca|8D(gmk!o&kWRzx@Y@D2GZklXj=E{(inx2^# z4|ZC7Mq*w{4yZ%`EvHJ#u`L>d1gsQeks%opyJvwuM)HtsJtk# zz$w21H2xN!l3J3QT*8o?n4FQy;Oyw^;mY6)WUKM%Sm>n#d&=f-_HlXP+uQb=n$}c|;w2Cg& z%mfs@Igmwv#SGrAk--e{@#(20nI#PI@!qbH3~7nQB@7y+W+plcrDi55rtgJvGVr2zaZpYxAT1mh;u=qDj&d-4b zwn;`}afWejVu7JyP-0$6elFN@(4?3NC>qS6(O?RW2GEdinsHKcVp58kxk++jsu5_& zH?x2NWJqceL$(1!wjo2A0YjM)Lzyu{UP)qR4nuHefuV6wera9`ybuB@x3VhEEU>Z) z&MfdqEY3(RGBk#^7m=3spJ8EE-4c#URe zevw~k4rraIUujMVXvt|_B52Vjq?w4^#sW=TftOc>XO?8Z);M{CR*+z-g|^3FDZaR* z$jU0TB+UXm1Imz+SyTY2fsmK4yLbjMfD;dB{s^?h3|cV4G81^A6KX35DM-?ap{**Y zZlW4j`1BLq)WUBFyy1mU8AEDbN_>7As4bhCn2Q?lpO%vds@I@xF8I2BkRkpBsddT>L{r zkXt~ILI^FHW10e~+3c{Ee6Yo=BpO9p0ZFbUc*;?7jf7R>@ChqJBjnJW#GRZ92#L&Xf%rG_4(v?9i)69fHt<=nf zAuqLrp*SG;e%GiY+lOD(ao0*P5!1?MMcrwvDwN91i?0H$Vn;F; zudyb`vY^u2v!KijWFZ!D6R>zmashaCQHqrn*rg@O1y)ue$pt>3(Um+<)} zVQDdZfMX9L_d7yky0A5bdkeZU7TFj7}n#+)zSecX>Uz!IV)6GrHD^1MFsfh4NgA@(WgV+3|rugNjq(a7jz%4qI0wpsqT?g7gW>h>F4GTnixpVj!y^g@AOQmUmEQk&xKB9LGyo*I-|R=|2C$aATNhDbqd zXowWy7`3T|af)HGQF4-HQks#aK~f^9TVZU(;27i%AGS0ut}HeIZH+ep4fGmk<`$TM zqz%n849qMo4Ug17!Npw6FW8XM^-K%E4o zKpWGcOeh7KUxl_pFl{z8bqsP3L$U!hgKTPPW@4F^Y>=F6Xl9sb=*m!Bng{O~m4ZfP zOEMVX{h!i2hLqBRoJ{bDLr73$yt9wLYlJIiP#dMCSb)Y5K>IQ*;aUt0k@OgvXBZ`# zC7PS38JMIQ8yY4hrMWWX7o_IJCo`nxfi@<@C+Fm+R-`5~_y@T9;jyaN1Z0&NM6aO< zs%AqYWZj158OCXe2B10kg~a zFQ_!M#G)ExYGP_~l5vVrQj%qIQj&$CD?@5t8EASvo59sD3_JmYk&KM;i!ETPEMTfY zNyEU>JlWJJ$>GT9WAni4@>hXT;9Y)`*1M;}l4YJEg3gJdn?>dc|)z_Z^b$%z(b zsfNjB1}SFA7Oo8Gsi31DQu1@-L0dJT`%B$jLxLRrT>O3GL;W1X96fzNEAKI!Y*Lh% zmjcQ{Mh4hSF*3wz3}`;i#3;qw!qCXlA~7+^A{D&OE-^VfKEEurC@m+yoWa-8(=Xo9 zCnPw;(b*frUPF`O5@h8dCm0%|=rJ_UNK7$KOS4QdGEGi2Fi!$4s0na%cLkrJfng$a zO@%4A?y!Wb0|kGYfrY81u~AZrWm1ZTfw?QJhmO>vO|2-gg7nx!^D-;oz4el!O3+G< zk_v|45XX>+co69dT{#kpS&kT`6(Y#q&CLuAj7$?POcKpNlMJQC zMhxkovC??xAt&+4#U(`y>3OA~$$)rd8SINNz@1-cS&?ah(gnu49s^aq9fKP~cxqy{ z4N+>*%ORuuVnYi|*BcpNb3SOPmSsw6s-aP8Vw$OuVWP2-D?>qkab`t)aY15v9x@x0 zic{kg!8?D7K_fTG87Y}X@p%Od@$ukx7Zd9;l+%(}OC8GoT|*dV0?Jxw(mXDJt$E zDu#wg3Ch?EiGv!si76?t6a^|yjZ>4763voLQd2DrO)V^tmT$#F)`rEy)}5rMmc-{~ zf)*hrXM-{+Lz0;ZLxqJ|yqO6@JVY@xFA_*2>3OAAR!L?iR#p(*hy#5ROG=93aiu=+ zj&%hJ%?2kuaD$p~aX}s5AZ)~&_NeST_`(u+l?tsP3{Bw6?!a+oWE77zhCxM^u~D*7 zs)31_Ws+&KrCG8oLuo+@XjTR?Ih~vjTHyemQjZ7kiGwXrKyJE&N2b8xZAV(0A6X-w zb~~~%Sfz+q)?sLY>0(1eBqw7uz|D*dO^qy*k}Oh^Q zVoqr)cxx$y58CEeoRe6b!BCU}YM3w-r4*;8rZAKgCo|;cRxlJ7r-HB%Lve9&sxgRZ z5^o5hKw_yTi44UVi42Kl6(DQ`B1{d9(x7y53Y3vr1Z5P!q>{>^A}NJXMp+J&k(rhY zHqq43s4OWr6DnF(kYA9TSAtNUl$!~&FFh>}t}HRN7_K8ZC$}`G5TPXxt}H3DBrzu) zsxT?d&2AKsWLB0EyzhsP6c%~l2ejXl9LV0latI$OjD9T>v6$ygo9>ehP4jlAQF<( zfTj*~ITL7a0dm`mVA2332~c{)I}QdOiNWe9aM@4Akwm;U5jSRt#|YG6XM$lvv3;lm z)%36r+Oi>N717fR_(GFd%P37_=zE-T7hIIu1fNkQ7=WPTrO3@Q4B+yf0pwT)4T3ho z3U9C`5(;mS@u;)MG`7nQddO4=XsHdTOc}g&AOomoL+K|F9StCzb_^r4dIGh>2{!#>EQ4B6=1uNVbAjOQxg_+VTbAQgCk_XO=MmC4UnG zq%;jVf)7-JBBfU_*F8kVI43m^G#im-kz|;XY><>{o|tNx2-;1Tnn$=r4N26n&IazZ zq=QEaoO6inX&{FJB!?4rBs3@pD@64ToS5XwkXlGMyMVH-2hurLB$sI5BREo0DKi6a8U)u_M#c!2g32IB z%L?4$$t;d9&PXguO^Gkc&o2SZs9KsCCK^~8nI$JBrKYA@x-y_Dp&-nuRk0eHpgIY> zQXw!Ik)HESlBWn8;lzWW~p~Wm{;sLZ}I)x?#CeHK`^h+e%FxP61Tp8|8^Fc`~#dxhjyIrM4NNoh&OMy9R|`PqbHbp$69f>~(jq!Q%$Gd0kG8=kqjr6nX(Phj;# zWE@x_fSOLpt)wWlm5iziY9pTZE8c>QV3NTeK6VV=VdM?ndxz1mvKyelAR(E7ybo{h zVVS{)^b5?5A*C3oPz1*izHR|DQxl~Y5}SH@;DAEvLV(O5XQ;|M3{+g3XXcgUL&m8L z%#zHLj8aWa5|d2L4bqZbL3g5nzRAeg z6x<1hujwV2$splER3S;j46Q=K#3!U|1r-B^kSWZZ)Wo!S&~9JQ{F6bFWlC~lQfivH zsi8$m3Uo{yu7pk{1HP&Z9$AJakP!>wBa4?p4RM^m>X>w9ZYN}DPr9qNW zvH@t55mb(TK@9GJf|>@V;2R!687e8wI4RZG#N03~CE3t2%@uN<9$_azB7?9xXqqOh z5Vgw2+j^&3t^=;?VDl!1>c0S`i=j76s9mgwn$ zPl^U5BIC4N(98trctpcQ3nQ~+vm`@f&@C_VQGVu-9UEYSkv3|8j``5j10VUUr-!)p zh*+B}3{#Q}jLc0eEkUg*3-Dm7sRhCd5aUTU*UZ8^B{9_`&CEQ-FxfZ_VlH&g31ko! zazuij9%PR(s16DNg#eN6Ha1O6Niq!W@;3{%Yw%u_AWOcPU0(<~uQz?1qw{unQ@ARiIIs>N~!_L?RrS4(k&|wtwy0inqi`4ijhgOL7H)r zxq&GKjY8Ulv!MZ`{sNbr*vA;ag}-M>Y7t7&iCOT23MoCklA^@Sl43nQ5HGPLzerCH zq!q*h)mY$u2ol0Ee6ycEMS@~z|9Tc!~%2B1<#qO#h{IMX^E*umZ`}p zrbfw$iJ-L*P&v>Eu_;zo*w=etKN1ahzOlpC>jj^F0t#4sK}OE`O8EL?;NAnt3mwhU zERszOj6vm0l9?$ev~oyU=t%Wsk1tq3D><#KVC`qLK_A*J7l-%}Z$q7a6%z?n8n_`( zxFQ?8iz3a8jM9=!EDa6K6O9awK-;THUla+6Jly%1WHJ#(1rb5KkVA~S}4hW=KNP-ChQnf>7%0Tf#ZhS%q)Zq)hjqqPeV4h)= zYM7X6Vv=NLnwDa0Y+^$6f@K;-CDQ(G(-fn`R0CsBa!gJ#105m{-TO^AK|-=3VRg`a zNLV2xeyps(5j&t=b&`ua9Sz7y#MoQ^3M4oGQFeplYnG2@&+OD9{QC(&(=%okDQ1=i ziD}6xriO+|2GC*`o`dmqq)5+$X+`;YCD8LB@s!gf*V*_BYS0j(p#}K7Nl=vsAEZSu ztw2lTERs@942;YTlakC+O%2mrM|(H0vx{M6(CA!ps-7MsF!c2B&LvwIq#76)rKFgc z8m6RJCZ|F37JPP;)N&tBjv+lO;ww7ABN~Q7c(GJ!vU!SmqM1QrilMn>5@=TrWFZwb zOEdEOqXdJV_`xK^9*yM0#N^auqa@Sh#1u12b5qbv4QR{B046f>LkO~TnXuD`aYKu| z{kYy?#5B-`V7n73IKY)Xcu1P?QunlEBLgF&G!yeAOS!8ccgG6VW0z) ztiYW}e1l@cAXVXCl4>~kmm{W_q?j0(nHn3Vq!=2Q8CVSbv?cMC0H|R}yP;axIxBh& z*TU@^aznN-mk--vTet^FC;^F`(;4!M2#w4w%~R8ilhRT^cjOp>_a}oF7!q!cK{`dS zQ7UTpR?H2-1LEjw*YbdNR${;V5%goo)17FOirw6`!k4QI| zSeh9c85<`Wm>HxRBqkYxm*9b$NTAh##(AZ=rjW&K@aY6lhfz-tDPTYcIH%vdwMa@f1{)5kDUHiglMRi+Qj2S3)^IU_SCCAcId zGY>Nav3kwK%rwo&G}Y4B*w8E`+1v>1H4{k8sQ3h^s00P55NT9Oa*COWp{0?rg|Tr` zvOyBWC`jn5l$s`ivac!V@?#?d6_iwj)nTcrDQ1SriD@aIh&2ao1B#FGvw%1Zl2eLv zGLuswi+12?9n-0x)jhoL~0{h>} zion_$@ChxDOZjjY|KJ1#adJUPkp<{hjl9yF978kceSYxpH3Z%8i#WLmw9?Z!#Wcm( z$TT(0!Z6Vw$pSQ@R*q1CH}Ft)z7pd)Q?Q>6jX{1!xXGj-zqlkmGcOr*$3zi$rL|$2 zg^78hp{b=|VyYR)QK(Arx(ZYfLh2lF#fFi$@=HrFlNr`DY;0zlmS$*>WRPZNX`W;X zEjJ*i6++BTEXmN*3jp6z3BQt(NW+bc&5TV_Q&S8QO%03U} zpowMJUD-sMYms7+YGz<;U|;gy(VZkA?hZjok`XkeU{k_z?`bQ=&!jg~Z{B#R_73j=ctgVdxHLx|JCxdQ7I zsNg(n0a=m&PE1I<2jFQI%7}+gxN;EJ`Ha0aj zu}Fr*jhP9e)tZ!;oLvGsLtjr1e0R5hQFvxbDzSl+Xk?n2Vv(9;m||*bXqn`SdeLlR zNrqEt8Uu9T6g>?(m8R(^Km^gReg?-ZxHKj_nwDy6W{_%XnrNP6l$?|d%24^)(7FSW z-a&Q|vI>+zNzdFUty0U>Bm;9ZLsQdaLrc)zH00(4BLnbhla>}~W+_INX2>}K;U{n) znL(P+;N+i{mZ_%)xw%tM&%Zdh1k!LO+|n>KNii`oNlG*_N=`I3H!%P^+Z2-Qz$Sw% z)zbrACQoF-NKH0NNi<0_NJ+CywJ=VGw1kX{OHz#UQcFxgw=NqR1tZ&E4%%;JKa#7d;xU}9!um~3vDW@%|*nwSWh z`~qcWNRtZeQ4%dkO)5=CGTtK5*vQDxGSSk+$lS;>1>$}@zBU0}E)AORHb^rvGP6jr zOiZybO0!4AT-niv`y z8M`tdq!G=rywY48#+j!T<>$sHfi4j+HBYlNG)Ofyv@lLHN=*jsKZnSH%z=5wG_NE- z1GK_A%_7k##n8~e(j?I$#RS}B1j&HZLcEZcTVm;0lwO*fnpaY6mX}%{UuFo}gJ)`# zm~3g9VxDAZY>@`qAOn#?(FpRt31s6L-2aeL7pb{{+)FIA0EG=GD}&OLsiB3XX_|qh zsgZ$^St7Kqg#;(G(?vuV56LD(hse;_IK?8>*fP;LF)1}E$qZ6gL3@!VkTwYEO@7Oi z6eDAcG;>1>gS0ebQ&Vt4pmCEwDap*tAT`w_HO1J}%+d_jd~ zF-SEwOR=;}O*S(}YQ>jYKv&g*TdvU7dZ~q-9Ybzu38?Rli131vB6FyqF(j7Yk*?wt zpppx1c0&5}`Dtmzso#CAC6N&l5y}x*MR>0BZ3R>**DNZcNqF1Fv9lE6vFPPeV-urfV}lgq6hoslNZkcp`v@-5iS#bk zTyC0@YGz_;X=a*YnwDyk1|1uKB+aD!{2a_s!)i>jS+ZeLvay9pvO!9kxuJCdaP8hT zb4*DgHkn$aT38sEnVF_oS|p`dq(Q1TDt28=Qp{4*Qq7VqlTuRB3=N>I9yNp~$mmlT zB^etSCYhNSnVK6~n5M!9mWsfkVgYH#gVPDV&O>s3Zf<^_o*t<8;G9o%^ElPSAjL8{ z$1+L1IzDl?GmOe@yY16@`ckXn?RSzMf%p9eYFk#OrRCCS3lBr(y@1hi(( z*w`FgX_z6kn$T=<$t-e$Y_}pZZCMzbSfm=8r=%L2n;Dye$`(+o38@c(WPX!P8kTj$M8TNp5kT9bGT=wED@(c{jj4VwO4K0mRjSZ7QD~Uk6=pn5ngr7lU zK}C9cZbkXI;Er8*W=V!$X%09yd1vOOVA_hk>@u`SvoJL@H8D0fOfmxmx-r0N7M z@{>|RYo}RrWqO=nwnZ9n;ILN8l+f45-%vsv3C?qARPshBt!E=OCw9mv^3LX zgCvM9 z7n&+ij)KmyV5C&2*_d&JKR1||nVN$}0gWwAB^miC#UO8hgWV)KBeBRFDry8N+2L(gkQ}I>vB*iyi!Uun zvjDB-H36lkq(oB_qvR9|V+&UXm?UXt8XBhNCFiH4!pt>IGf6c|OSVi+HAqcM2AxZR zB+uZM$KaOdTUx^4mgk?BoC>$fIKQCSBCRwJd_%4oMA9-jF()TJKM#BqS)#F-v8knj zMQVzLd7`?#oTy-TubbwUmZVmg z`hrN4ocv_aVW1YEdea~^$|K3XfQqEv9eSXl+7g0?It=9Peh z$OhB|x3Wr3&B*~trGanl1dow|2GNR3Qb3e%Vr5b)q&@<1p`yVhDXB$8L5bydc6N54 zqxy<6^NKT*5vkeGATtj%2?-K3v;4D2JJw0&oyaS&g0j=pU1}Tfr%!>zyKd2Rwl44+HXlP=VVvuHT zl5FbAfT9Ak5CS{T$_n8;aIpbxO@hlX&;{ao$$EN@MTj*exSAk$h5KokFr=Ef`6^oz{2VIzKWNDUWV3Lw* zoMLJQULp>XfH`-NdcXvfGRzW7@^dqj4Gkc4d~s= zX=Y|=X^EC8N#;oq#}6i#BDFj%3@k0pQVmj#Qp_xk%`Fp&Y(mp7sSs#6TcjEprJ5!s z8JnaeB^yGUrBrP+nkJ=L8iE=$Mn)+qMxcQ$a2o*J*N401 zG|xyhFfvFsFtA8YGcY$z0u3O7Bw+0jB3g#@aW|w91tC$IQl^IH#+E4t$;OE(X5a;( zuyz*A3y`FZG+ab;2ZNe^;PxWH;ZRe{Wb-5wV^gyNl9vvjso1tR#qjI1*yeW zR^TZr$l6256a%E?2X>GdWQC=nDcX_BhzYz@gR~S23yVY}^Q6=ybI=+V$YH^dRvKvb z4m3IqUX6&9tFR6qn5CH}S*97MB%2$ini-@eq4yyn!>L@P^8wz3LKO$IGp zA!KBVfn|!NX{w=dig9Xc3TWOM)osu!2r*WTyfOvcNG4=MqCs*}vWbDAS+bd#xrMm} zx}(81;Mi~gYer));FB%Q6AdlXl2g(w3=C31_XlHfJ8D#c@;{`cG6&5RfVK}=1|^n< z!FCWit2lxNqzp}xON)w9^Gf1#5-amdK}(O)EKDsxD}~G~OifIaKnK;JDzPcGfNeE} zMg!#PMsV;DWhL}_$aqK>1T_9`Y+{j?lAM@mU}$Wd3Yz*vRf5+_bI7Uyuv_%>pq>S- z;lc1MNyA%7X+}l{hAC-@mgbO6li76H)#;GYrMn(q4#ztnis%WU| zt*jtX1+ooxMk3gMR#wok1_w5-CC{+c&zSy&%m|{+3=^E=F-S_bG%!y|G)hTIwlD`J z2zY8G)LS+*wKO)hG`2J`PfN741YM>C*NU~EN;0!dP6MR^&|3Kv(4I)RdO}v2n^~Bo z87CR08mCz%nt|@x!|a$F=xBgeI6;bGP@O|j2RzZhAT7zjD9OOY)WpIRM+Y2Ho{%*T z3dtg%Ub;_aUTSy|Xx|g00s!^l33b!+OG}{rbZ~kBr%$3f>PeQCNhzSREY-|3#XOme zj{0EnH^Sqfj0u}AOENPuPccX_O-?dPO*S($0(I1AJY7b$m_+pvzK(yAVTy@Ks-?ME zYMP;ip^*`!?~FG637UDs-<=+WQ3>ihKnDm6Of6Cp(~`|nO)XMVjFL zAk75Q7D2SDp=|?DP*HQho@(AhEwV8Emy&3fVr-OZVq{`yk!orIZEIopkBEVQK~q*C zc^On#ff604p=)NGXlk64mX>O6nw(+@Z3e*f22}bmfC#T3Vt3aisvN6<|9+Enno(7PEr<%;Nk!6SthibdwawN*r?wOG^t2 zBhaz~14EM((2zlX9(a+RInttBP>F+WErd^q3XX;Tcos!~<|08CTcji=nVVW9Lp%L8 zpu3Y{y#T_aCPt|#iD`)zpbY~l$tIw#GIZ1gGR6T~Ibvl6S~&upp@j#C86==gbMliD zbHEGYj0`~w;=EHU!Lb8hX=0oX-l1azx`WFy(a6%+BFWqWG}e@!T7t5Wjj%0giNz&` zM&7B2Rd-Os(^5>*3@l7i43kZbQVh-0Tp92fu9KFhp{by22OXEf?L|xQn#OF12fYUa9a!9=s;PVU<6ylP?BE+UXquVk_=i;Wtoy{ zXpn4}G%`p5nSzi883lF=G2LAA6hp%l3nPQ%RFh;2 zlO!D7Tu8xzmWm;Q7&!oP4IJ13pq3oI&Y5wVMQU<#im91_rJ0E-X!#VVLxZhz2C<8D z2Y}igATvSh$C8W;Op{WLQcX) z2GGPyxFiO}ab`(oB4{lKXg!W`QetX~fkBF;af)FYXhjP&_n;bxS?C&?fXW2WdS|#< zmX@Hqg3MFQj1!YVhYLfB4TKV$g$HWchnYY?2^4z37P!@}rw87x>zP-Qk8=D8=GYyc zn$y5MF~u^`DAmH;B-PZ!0=h=bMgzQXv(!RI0a`GpR+OX`v!P?^M29i% zk$f5rs~aR4Bpaui873PhB^gkcDsQb2KE3Rv(3{?)6$X>O^rZ(+GGPG z^k79dyCAb5)ym2-FF!9Jvmh00I&`BgsoSQou17Ypur#(zO)<4FNHIz^1IDNZ0Ha>q9@(Ztfk&?L>m z$k5m<%`g#ZQ6*S2D96*R|72ulo|bHClxSfII=R9e8rP7HCa9xkWtEgzRFs)oWM$<9 zVFZ^XmOvr`Pc$1O8>U$pn;RM@85<|1g0_}``mY#C2z^BZcu_oI%fZP2kL{+$$;n2b z3;ZmT%nS^0*bY4p$;t|LOhINIA#=@>O)QO#Qp}PqObk=Zji8-Pgs&h?m!j0-(wq{| zo+J=$qoE1C5(YH+0zN&`$_l*q6p||Ncq7fs(g<|hWSWs#vWbZy=+Hz0i4B~4knC|T zEiTE=C1{aFN}^>-vQe_Jp{Ze73TP7pme@ivHYl~Ah)`IiSQ?}lBwD5#Cz+Wf8{wGP zMlur;B%rj8Cqyh$%nXezEmP9WQqz(RQ$Yg^1VRL2EFs5Qm?xzqrkEI-nOPVnn!_8sK$t&o-Rpshoxm&s)?aNN~*D0nuWPxD!2m(YH%B4 z+ZO}6?!KfbzY;WRX>MU?mS%32m}Z`qoCw-7nvz-!k^{LEWB{&?8J?hCYpd2Ily8Hn?_^ESwe}aX%@yNDQ3n7W+^7dpnPAPT4IB+7}^K})p&Y( zpdPE99^|sZpwu*9=pnV6W823oah zVUl72ZL^?P!yre2_BIt2<(ESo1&(w}9DDPiD=qZ&P|NBYooaLxp|_2MQW-g_(Vg{!M~7GhhT*ud~+pyDW#qs zXm6&T9@y<9RdxmzX{pJn7G^0HNv6h$pr!cnQGS+?!!E#e2li__A$N@<4aUIlFeAzK zM9>MF#zv_Ih8ExjUywM1w1%;+0Mr53URb5TVT~t!q#7os7#V=t7fFc*;A1^dOJfb( zMnhY(SgnODl7iR^D*ix*g4zOUX2upNsm6&ZDdvVIpxd-T5;hcBg4)=D%>?S{fzKoH z$=}#}ZZN4Gjz}l2g(QOw-KFlTFP*i?F~U4ReT+jlVD@s#30ScEF~!=E!h%!s2a#vM2QQ|9H32dpbj0(l{~Az|a_UKqab;*nEW5Gw{`0$z}$oiN*%TmY^;_s67grMTO0xC+Fwnq(WLj zR#r(RMX9M)R^aREokEIIQ+*Q)@MS~j92(kSJJ^L-a|!6s3E~SDmxKNw^HNjrl_feP2P=Fi7h7zC?p6lHX;C(4 zECX~mG}2{DpxPAD!Z$QT(rgM&3E=zoEDfPP1Vsa+r2{_P$yo&wC(bJ1Jb=A|0NIVW zl@Qf#P~+S*Ex!nS%8Yr6QIbiTk*S3#XdM@54Fp&QUn&Kejqn#FRH+)XprgdWe!~(p z7I}$fndylosfN%K>EL}7unv5oLBrq#1*xHFT4qskNqkOfVp@D^N;+s((j+y}(md7N zz|cG;)eJoQj;3ay!`aY~xV8q7MFMEuFXFxfXu1ZSVF{TwN-W7Q0tJ<2Cg}P_kYYpg z4AbNkBSRB2<0P{b3(!oXE9iKf{CJQoz9JCR53sT#rND@f@-sAqoD+p7t&q~R0=W_4 zT@VlCL&&WVAQs54CZH?L4b3x>jEqywEes9K%uQ2LEkWHI;tOQtX*WpbcMrjGF$SJ? zhGnXyWn!A4rKNeYv8fs8)V%m8KRlJ8nYn?1VXB3(ajLOVnpqOArF_sB2A!CQT8Y72 zj4uXBE^Q&BBM?_3we^w`O;Qa^K^{y=PBQ?_kAo_1d^rnT&(g|?G`oWmQqn5mDQZa$ zQS$3A5_Gzzq(esU@z-~T#+Hzv1J!qUGAO9}0o^Q-k_tU*5^LQED(u14B`BwmP*s8) z3(B?@MVaXtCGpv1hUOV2#)&CLmZ_$RMyZJ=rskk!_)tlFi45dU8dj6ADN$@$5Y=kb z+8^8u#u*dv>ea{;RBpv*mmzLbiHFYbf+u#3ER!sgjS|f)EJ5dof)3%V0EHH&YJ4RC zS!Fm8Zik-iOMKISyo5(EkwPv&)6)azTop1KZlKhtLPiP(=`#e^K8Q#mxQWWd*wi4+ z#3a?sz#`Ecd_WsfjWMJ##1um#LyHv95-LNBG~+~2W=VzY2>=Hdz6(Pku|POaf%0}x zY8qjMknp#%0*C*Aw)}~?g%{>pP!VLOgLkNc)QqEvSS?7#^^=1j_!vgh2DldqP8)dp zj))R`v=ju@{q7+u=%t{cc}7Z_DQHQOfuWIks(E6n8R`UpQEDNm#cpU}keFtgY?)|i zVwPr+=*o~j zB(pfaI3uwrH6^|%KOeLXJ1I3W#lpj!?8j zB880{#K;(^*ikA4ciE&P99_W<8 zq{O5&gCt`M69bDR@WFjKsdo@SYpnr3EbU=F%V zCOI`PWvGS_xI6-_7K62;s8mE~W7$>HgS{j%pgVtvyra%|VkTMlAOba8lLY;_O z3zjv!!@xBF{`_iaY6uB9P#*|feBo;sLyIqvTC5|~1Py?=O-~QiSD^N>in9vDA+Wv+ zd^*=SImJ9JIV~m0JQcJbEY+1^pr><*@2C)p1H{ONF=Wz`*5kW0h&G}l-8&2%(TLFt zO4X_yE)vKX|l4ctQ==zxdU7?}Hr42+TtlTA`UXP1~-8m2(c z^gt}AqV9MzI2k}(gEUi{nrxbuVqj=sYGjmRVv+<~7DO|vv99y7G)PUeG)}fmPBS(( zGD*Q$LPzRI4A`rXiUUF-FWEOrNwY9AF)=YUO#)pooPxB|3UcKW=-ddf2~=H_mz6EcXkrZ8#{(G^f$Xu$EhtX5vPuH2Tg}W%hpx>7I|LGIScA?W#n?2(BGt^y z&^R$IITd3WG&nP3S(FD_qleu>LN+EQ8mC$snV2W0BpalrnLxJ;BT_B6F^kpn+jLsUg|O zEXl;o$ROF$)HEp#bmRi6!6f)2wWx@oKMW01k_-$|jT2K%jgnFl%^-`F3HbwLGJ{rf zeqM2j0^V{491K_o>opnt^7H&kb8;wHJ{*vdm{*dY>ylWKNP#KvB`f6m1e`P>rIaz~ ztW|TEiJ+<&yx+jkD6u#mbP+V{c4MZ!zga<&kF()RMAbT3rs)l;P z!qCJhF*(W5)W8ySJrL3fKA^D-EG<(5Bha-Urm3cBmIh{q7zc($lUv%wBF^#vXC17i zJHBNhnsyAZLl($106qi(PVH1U>%$nd#nmt^)xtd0#MBIQJ~;YWALP0e>Ov?*@!21V zX-S|aXo^XanT3%-BB;LsnW>A1oYzQp9)t}jU^wHAzi1NK648@q$`4Km!BgY!Gr21Fqw|VJ#u3eMm<^B6Vl% z7~pj!0d?@jWkk0|EG&)9P0SKa&63TGlT(b*S|fz5qxzvnsb;B0W@e_z$!W$0DQT9l zlVS+F1RmQI)zhho#;J)WiKa$oDQV`3pdHA_^)z8u(5W&uNHR`JNw!Q&O*S?KU2uq8 z854FSIH*XkO_P(2lPpq9(h`kQO$?39&G6Kw_}u_*xY4r8Oif8iOtmmhOf)u2H8-;$ zSY;ZS8=0i0B^sKW8mAdpg3feDEe@jb`X?6bA8?>zFKtYX4b4rAOp?-4laox7KzDke z8jsg>u;GwC2B`;GnHZZ`nwXm=r5U6o875hRZplNr_5s!s!d~p)t{8}N4Rpx~w0bf$ z&oE9-N;XVMH8Zs|uuMrz0-e2rT7%&Y2HdWpQ8krfVU}!ZYHkSX&Lk(A!Kx`T9f>D( zfSPwk<_2adhRNpUNuXIMtf>RNF2e5+Ton#z(~ddn26{a`(3Vp8ffaBD?N6>qEGa3% zY$fB_c9UpkX_}atn3$TDmXu_Oawi4Wv6@8Cf_l?rBQrAt%cN9LIe;3pScg=g0SoRP zz;2lVXF@!2XqJ*f*5S!?cq~jcO0h6cv9w6F zOfxkxO2O!_K`lh2M?*76@*pY>QM)ssC4VUvhQ^@1cA=1HIp1*l4FKt6%m38j!a zFL?ZsXp~}RkZ1zB&DIFCMH_wa2b?64hFd^e*j+MTGWgTHXCx3vz1i{_;_tXMw+Hs7^WtJZVfXrOEkv3n+I$p^45Qd@$s2?Y56ca zJoD1>3E7iqXljs}WNvC{WNeXa06LNai_=r{%B-xy5{nQAOW_GmR4h|K9MG63Xg8~wp^;&-QKCt*rICpdc;*Er z3z>ca4?-lz7nc-SSvez}Ib>lF4;q98r(o!)7!l6N%u7kF0Bx{MNv!}m$0tO^6EqY9 z!j@o3sJo0!j8amKOwEiymvos}nt*oMrleMYWO2F+zA3-h%Bm;Zt6Es#|GJ z4mb#*NAi&9W>9j%yldXf)DnF8o1s~fu~`y)7biGX;|eo8X)e{wAjv$jP`hK<7Jf*^F{7E-ppzu`}EXpkrgW6i|Ka%hWu@%pf_{IMLKHH8srwbPzUb z_W-9$z$w(q3Q8eW50J8hKtBbeXJBAyWN2iRoNQ=mU}kItKJ^W?SwXr(Xw)f4HZe8; z-<4}%nqp#N0X_T_caDdK5R^g+C_Fj9B-PZ+*wE0(!qPI;)I1T_wJ7Kf5S(E|UaQ1B zHO0)>)ZD}%#WK~x*aVj=;29p8M=~w!;O9!ig-|jGToj%okVK$a04_rHG@oRUlxA!O z8t^hRO*RA_iH_F#L0AoqG$@4>cjP5DCZ*F_@W& zp;1b5k|C&h3#uOx9V2LTA`N*!nlSKG3(iO2W1bRIQi`msg7cHJQ%f9EQi@QLF>Yn> zbd665G@;{GLiLnyY><>>VU&_;4mx%<1=MOpOZj-*0u3}MMR8UDWk6#KLnBiIOC#e{ zlO)i3FRqZCs!Q!~RvqZIfd{*bXP$YGrr z$6-PyCn>+l6y#Gp`NuTX(89#T5>&OP7^WG5W=Bza4X`8xPoLzUmPsui5$Xt;TbiYs z8W|d<8YP;hC4%k~LM;*?!H#X00%V{9HbwzSV0gkV)!4|wGTG2L$t1NxDbd6t2~->4S8rizo@Q=o4m$D1#4^#s0=zFCOE*6_ zwZtbi&p*$lD8GQD5phGKB;#b`G)oH;3**EzLxKlvrx<}sTQhUBG-LBLBiO~5h{%SP zQQ&4hvZDyap&{s`VbFx6g^`81K@zl@M3@VSL$uTawG8dVXs}ggmf+zV=oztj$)KGf zc`0RyMS6P9`CtY~!DMc3oMe$`X<=+)YH4a{ZUBx4l#^`DlOe{MLyZS5PX`@#4m#Yx z(!j#Z!ZO7;G07s)0u*M@8-|b%K*ys$)flwYHrdD^EhX73*#hlw1k43I<|&CL<`zbY zX=aAz=1Cy+s5u`RY)DO5jN%RI87nKOZy<#Lq<(>r$Q5F;kx7b?k+DT$l38+UBIqI^ z`1l@SlR+i3DbkEmWI2Cj*AZQDp zaav-kaay8*nW2fHMIz`n5~v)?6=k?C5Hts!KaigbI$F@cBr!S3C@ICz#L_e+Eja~p z&O&}}Dx|3gx;lW^d$&O$f_&WvC`4ejhMt~NX&Si4Q3M*E341%|Umb zq#1#Zl7o!0ljlf0l|qtnk|n5+Gcg1WyMxw2pr%1^x&Q?V%wtwoaF3Dh7;u)y;~HZ# zBTGx8M9`uGgH#h^P=^7tdPj8)MHi=oV-RyOEpi&f6=|T+X*}cECT3|ShUUiRNd|^V z#)(Ftg8)G~(8i7Piy;Hr{>Ahg(FQF>PBb$%PfSTkvNTDyz&@gl?lQWLX(y)|C7UOw zB&MdCr5YQ-8rYEJ1|GOYcO9X0Wnp4&X<(FOX<%w-X^Q0>dZfW-bfaVKFoy$hxB+?r z8aT_~P=Y)hfK>_AhXYJ3(~=WSjZ-WWO^gl9K(}LI8xFwk7H9Ie62$F7Jmp=o0jNecu}m~f zPBpej#whQgA&+%90DF`XGaO(J+NV%ZQe+G%`B0X(=R$9U055I_RhxOGIXQ;r8L25r ziRLD$W~LUFY38P&<$9UL@gOwmXlv@ zoS9o-XaufkG7|Gra#D*xGk``G=AeXXW@eCLiUT@0EH%yCIN8u7$-*KLwB87{WH)X&=vqfu7$+whnI)!}nJ1-M znu2<0p!5XEptzj~PMdf#WQw7gvAF^0{=G!Q#6-|NMj%rW-5Y3z1b3T2Ylgrj79Q&e z*=J#3Vq|1&mTYKYo??^+x@H>HK1juc+X8}yd8X69*z7@dAX9suP# zNb{V)WFKm+0=}=+%*Y_kD9PN&(lXiD40BN;0jqHrs6@F3WAf23#lXzcG$l1T)yOc} zG!+yQSZW{KL5B5D25)ZP)v?MTn z3jmkWSi>BgF(75}(7JHKBqtH=z6n!H6T?LF6oW(~b4wEo&?N(?N?;4mc}M& z2FAvwX3!Jb5o$nAvV;s;L!1O&-Uwdir>7SLTI~laI!ZxXT(OV&fj5Zj=>_LxCZ~FW zmLY=FtCX51sf4JMnx?3Lb-9PA7^kO#w&R)_7$h2)n3@nIMvKJDJjjwEXm9$#U#a*0b&}irn?Q;Bajl*3=$Dwr#eEMim}!c z>=dZy0uqZ#G81!L3qc-qgn7#}wXif1)T1;?Gq+4iPBAb~GO$Q9Gc^Gn6I_~@gT-sm z;SvW3F0!?O4PJ`NU{XWL0#=0q5?UoIMv+Lz``^!DJj_ibauNT z$O$&37CNOCb_^ifib0VL$&tyq1*Tvj=!6A`4Xze&Iwr_X#R=@`QX>NuxHE#>RKP(2 zaR6vQ#L~bx)xsj#*f=@O0PFzJRnj^j0$jk-C9Ro)n;W1D3XDL5G{$C$2B6_V(4iM# z87z?kz10mZ9Ke|YtOdHb78Li8m9;3rfSNer)|;lK7+WM68YU%~CK@N2z*gIW;sg}V zpnGf3S~JKA6cSU#B}MSP$CmEUIjIb#C21Cr&G}$fYF=`F zN@@xNav9G6YOIyVgEr^qWag#Er{)%vRDz7mEY3(xVF)hB$t;1L7-f^3kyvEM0NM?Y zbAP`DOx6N+41l3Y0i@uFPtGlfPXgUgY-yfmkZPV}Vq}(-Xpjin#fz#0(l9JYEXe>( zgyk0G=cVSA6hkgu#1Ku+$uBM~O4T(0XD>*af#+^h$ZB%56ay}5{EJdhQb#7Nh&N8o zEdbRV#%2aaiD^dQ%hZ$2lE71s=*F9aXFIVO4=u@X+G1#k!2Up4qYw5Wk`q9w8qGoj1H(kqL`ze{l%$j- zi)6_C(qR7~?=Zy@pUCDIS;VW9nkT7%3202Hl$xi21X5HC%`=iM49yZP5>wO6lZ^~h z&5T?bs;i;uYHLY}XVlm=%gxM#q@~mplN4if0}~?y6ARFt-L4D}DM&%0la{BU2CnmT z6d<&wg03C(exT&s0xK*3qLkF4%)E3|^NszBQjBvmK{J(>sfH#77HO6iritcBW=5%C z3m~SW>4B(Ev9Po>Hcv9NOtv&LO$ChsgSt+T3&Kk+Frx~&_yVOC_Yf8H6HP6R4GqnZ z(jG(&mihwyRs~4mik|N9R1)YJz%tRyIMKwy)F?UC+zh-*3GUF`0+<8LASWJzGeKHf zrk)<8+c1bccv`9^}ut+sFOG`EaouLAn*2vF;t_A>=n@Cj(!qxbTJk!)dNMVy^ zY>{ScX_01WYHDtoWNZd%-^C|YVs$rh^_3Z0GqyB9I_?R-zl>4~2^Qd%#);-8M#)J=$*GBkmZqlQYS0W)oPk|UYBof6xM`As zshP2XrGB^9nTVez0K_-G0W;1|tNIYnehM~g34Ac?;x7$G4O~GrF z3=PVQGD}k9K@E0LQ`XeN#Ku%07VK~Sel!sS(v0+fEK$MS*E60AbSNA6A-syu6={K&&VJx)i5P3 z*~rWQbX1MGD+9>ku=Wi^H~y(P(9H~~MP;e+nR%cAJ@DX*p`n3ga-y+8nxSE;xdrH= z>7vwP6h)vuH`xDVZvQif8VG6`7^j$98YEd7n45u`-jEg(#O0X&M$%_)Y;2aCVrpz* znVOnt0%}Ra3Kd+92GjiP_soRd|qlfxc3Y?={PARCDky=A}Q6xz}yTpu!E@#ZwNy& zXmW0WS#Dwlq%mP&l4O>el5Co6YHX5{2HJ`Yk%D@PqMl1)1!(Xm#UeE+B_+|q!qChp z$tcN{AvduCVmde*!QEt>3s#?GVxE|2keUd}JLVQfkj1u;FoTfhMUWX*&=_xOvSDI! zN|KprVxo~5=tPP5C_iWy6`aiU^q>@EnGt3#gGxK*E(cCZvNSX| zHMB4?GRB|A3=N=%b%G8PG0!keOEj=BNi#OEFiuNM0xdT~NW(Ikp#fxk73zwz)MPz9 zT+0NJ)dUx$Cc9RYq~@i7M#3Pay0Z$X@YBXxd#RaL!@u^^~ zpd#7aJk7|^Jk`*^z{tSRBGr`vNgn1%NT_4xDMNFJIp9Mo%xytg9H|Y1T9wpjW<}N znwY00nHpPyj&Gns0)?BNnw(~Al4hQ0VQFBRW&}D#99EQpPLeVO*KVNE572RS=B8=p zpxW0w6;|(|MngeK5z#BSj1yB*K#^f&0ZKc`sb-1EsYaj!#1m6eV5ZV#gxSy>Wv~(y z9iXG4jFK%=EI?>K1~Xz`3FpA8K%^Gb>`^NKT*iw!N4^9w5D^YZgREAMh3`4gPh zQ;pM%jMEIwj1toRq_`2$5bO)e35V#sbtW0PQ9KHMoc3zRbofu{gdcH4W6;Pc^r&NV7~dOR}^uP62J#gGfQrC+5&gW?nk3(HGcIJtWLX za1FM5O_NPPivkQ2O_NLw(oBt98DPtW@HtEYoY=5B8k|>gy39B?vB1a>W(7)685yMJ zfo8G5x5vh37RTq8=cR&b`c(5|bBnZOV{>C;3xiZpGX|SNe9onTYfV!@XMcg>HPJjd z1vHIkW@?;b2%YP|>3?uKF`?ulT4U41RPz*b6AKFiGoxhCSqPw!W5m6?_?FS2uIus4 z%`Gj#lg98#f%5^rYcnA?R#{o0-dKe{aexB{M<&WG1rG}unwy$fg02)cFf=g$EwF%e zYsig!aM7!w$$)$(160h8L4#;d`j(c!H@Tqnz>`XfQd6z0P{SQHNKh@OlbvMT{0{ab z?u>+Qgz&~O0B(<2;k z=MDVv3a%E63_;lfbORhpqro^YwH!2*lx&ieY;Krjk!YT3ZU8z2J1@1I+)4pHoI*m& z$^tY{3c7R;H2iLAo@ShslA3C0nwVq>I=L4nNwJHd?F!S>isTH?y3F`oaH#@H<*Amb zY00U^X`m~ZOh7j97s!5uKsRd}M zXgT4u0gD=Z{=?T~BqILJ%|LMv8YJ}zQ2~1zbgV*7PHHk}LcUl}4_wvi>47r6o*tz2 zp{ECK;;1;QK-%NZDzHm34NWrhO7i2gQ!C-U`eX}p)8rHrQ^Q2dq_h-M&?b5mCHVY? zJ7r@Xmj^o)f2)9q5Fx(iGs#Y^ECy|OG)yu!OSViiG&4>%F-l2xWdKPKj$TM;T3OM( zDzIZn1WmuC7J*l-q73SR*4XLk!KC%{T=PnEQ;QNyQb9=?u{slFMHoa6Xq{h4v7R1? z2U^3Yrw1CX2C+aRWhS8OGeLv<#+DYzmIg_QNfw4lW|pa-oKR5$U62SKcC)eq>BEyO zAj2@os~a%a31sG_V~*;9SL~q9=_V$EMv9C;_d6t;8yZ8-N`aOJhDc3LQ*hb>mzb7@ zNI8bu_m?7WnYFO6OieL1v$RY$Fi!&2DkR=AOLn5NV}R_6C7gbt;Xqg+aze*bUeTtl zOF}EzH6=Zjh!SEbTvBY1nwFT7Xla_1Y5}@JfaFVx$xc4Rm&}mRrd7`jwz8N${Xn>F zgSH0Fvt-UBewb^v>VN-d-}tEUIL3MDWs zAnFqfGlL|9)HJiCwB*!egCxi)vfvs4R7R6@-=!JkI!o{ftC@*;qLFzj===xE)FjxA z)I`?}B-smHaAuNdY-yNgW@chyYMzz~+5ih$a7Li+!yosCM&Oa8ct{ls8ppOUNi;Jy zG)c2GG_f!Qt%pTcK*)ReEgO~z4b-dvwOr!UGC{4E#6%NI(_{l<^R!gZy4fVqfgmtx zJfTNu$_buciC(0LlvY8f?xiKCrX`!En52SE2!t)N17!fB4TL7bG=mg_L_@=*R1-5Z z!(?_ z6C>j!LrXIwW0O=9OVC~9;8ih%48ort%}ppT%;4ks7U1#xvcw$7cs^*AA!y3WG7Y?7 zAPscFx=AwZd>9mEcszk8Sr9*Z1S&?s!Aif-uqaE+DTW0>lBI=3qPdZAvY~}(D(I>> znB*YwIPo<*%u~$`EsYE;jV&$AK^YJ%LtgHqb3tMVDOTarq|h=2bbqROnyH0pa-ylBG3da2 zaOsPpj6DC5nwbcuENWIV7NFTuXb>bB8l|QsnHw5fCYo9#f*OM`$wA_A;)_+YMDV~X zXnxqp!qOz!+&nE6bZG!+k`W?BS{a19MNH57lz9I`JCw<0X-Vd(=9Xy&$rh>RpyP4} zTZa-pp@6rO4(?7H8JY~v0u|;5P!UO4$1*iFG0gyUz(}G&vZXm_Sr@^=z{nJ4J*Wx+ z)gSNyQ0V%X)Z|o?q%>0llVsy0(?rmH#*knnoC8262BoGERtTw)=v7_AJZwkxD%;Qq z5;UM-Bsiu34IQG?LIQ~FDjViwQd1aOwVjfdW?*7zVUm()X_=e|x|tiEyzs1_GDZul~T(yn`$@fp^_V8sI>5R@2f_ zlS~Xuk`pab&683<2R=c@T%hY-a#M4yta1}8lTzbL^D^@?ORTJXLA+2fhj7|=%CAUG zp~#cq1`v6^g!Q07_ve}!rh!IUOjBX|1|i$g29y7g`}2l4Hocjo8m3vKS*9A88Ca&8 zCmTTe_h@sOgE8(9=TMM*dKi2+q)`rN#3wDu!o=LdJSj0PImH}yHVZg`fR6w%1)p+f zXaG542JNg4&q8Iq}ZcaWQc zHmQQHhX8FNhwNKUv@`+T%W05mlxmu0VPxvcfTjkNKEMgs7_ZTW$r-81*{Lb^tGicRlnq{)FsgaqnWuk>ivI%6FCL}To zN{Yy=lr7T|%}fnbk}XV(lZ_3{6XCNR1ltvq*SDakCC~lP>Nd&1FxAA!*woBC&BWL+ zDbbYyG;2a9m*Y>A7_%=XW=RI7NhT%+#%Tu0W}w^4QD$H0{?l2${5?Q#3-4mIfD}m`l9D35%G68!(Q{ zO-?d4GBUSJGfYfPOf|DGLOWk!5VrP+J7@vJqZS56=BXx$7KtfFDQU(A=m$v*8jtFL zqZr&Gz+c`FKO}`1k~1+gGqp%fN-{_S?OFugtTYIRABj)DGCdO%|mMJO5Mv%k7;=$7NZvrN!87HTvn3)PuMM?_jnhMP0a)@c&6IaM!b%I%vWpZMQu}Pw-p@n%;nlW053L5dp7kI>Ge{4zL z1af`EX|BkO%g55Qj9E2ObkKCPLg_@ z0;nqtI=ct7^Ulf&`AA&sCqm#a7x4H2e#nBku~Ay8g_)^Is%c`fX$quWO|8-v%e->3 zNs>uwYNC;$QL1sGCFojj)QBS7s)3ART3La2AmW?&AT=;an!MvFzIq`HBGfpXAXI7tCad{Sfz`+~(JTdYI1Xs? z3Acst3zDc)kK?LBkdH=4PBAh_HcK)DotI{8Zf*$LRb3trIyV7d3yai1M#@ci=ITt1 zQjJY4O%2UVjSY=bK$m%ff{S1g2P)b?4SakKhF-3b556pbs1_2Eld&}Oz(Gs<6A;W( z6OD|LEse}A43ZNKV5=qJ%X;wnnA9kuVJn9CN)~o5f|-S(v1M{{l5vWKQK|uGGa_g) z&yXuYiAY2AITOG#%_zml(9k5=40H!IXw5%$&jcVfu?^&QAX42*e)E$AouF+RM5LA> zaDIScim92AnR!wg=vc~B(6NvMo79Le*C25Ks!6E3Y>0*l4$+l_Ed3!|LJm#u(I6)c z{SEs@25H7ADM?1grWO{dpd}3{so-^^gcAX*Q%L1*7`Om~PnO}^#&2wHV4i4UkqWv+ z%sd4&lK|R)$PjdE0BEmH9%$(VHM$wsDWsix+JhC^z&99(qa3}x^L(6HRWjW{a6 z$kH^$0CcdqnT3&wg*o&-Jn(=Vt`ssXLj+WHfhtDWo_0gS6eF`lOQRGc6B9FoL}K@} zlU)Ae&F0_)NX^}J(7lByd$B>=B~6VjlMM_KEe%W)4N^dd{e$-wqA0^xx{>N@;ydi1 zfnOprDK*0gc_VjnlCepWxsj1sVp5{95$K>zVm5NqAvnOzEzrt4GMBf47VeuGm?oK} znHr=STPDMox0331aO9BZ8t7uxR7-PHQ6E< zHMcNLN-|C}GfOlEogD*N7D|IyCcgcGWeJK!Vq&5p=mKhUOA8Bg(B1UpEhx0KADW=H=mIfvUCT3>lNrtANqclMWBv7*|2bcf&%Ux<# z&d>%7e0mhLP7t(-J~7oGEy>&{+0;1MC^ZE<%z=E=JV+TeeNAe1B$x`R8AixQCYUE% z7?_zG8zh1%ltfVT9yH53=!1j!LL9MbJK5aWFgeA@$lNH&z|7Rp(3OG8tF}q4tf-zy zhz}KL*W3(rU$mK-iIKTknvp@O=}=k8hM4mrm~n>fCO4!mda_xHajJ#6VM>}|q6KI@ zBH>UWeCR@IvXMz@YKlpcfk}#)iGk|?9!pGe#!>)}PY|;v5}uN%-fS4=M=>O)m>XLd z8CoV=S{Ry|B`1P=50H8u&+@KRixi7AQwwtggTz#e6wpoUL@n=v#S&=d8ecU{`Y0;6 zc>?bAP|#U6HaAZ)Pc<+wGfFi!Pqs8d>nzjf00UApi5(?}Q$V|r#-KYQ%*{=clgtuR zEI=y@l8R^0xew= z(^5^$(vl4dMJh8H(OEO0c=$fXQr5L9fr>3Qt znx+|;!H#Je&?u+UmRXEV@#dCB1}RC#iKfYk$rk1osc3sup@Zaux_JQ34a9BN#qhpm zvbkwWib;yOiIJ&+DQJQQWw-Dk_dZQe3P`drG&4*!H!@5%Gd4{H9auMH#++b%E^s|Y zpxieP%FizWoz(c6W zQD$Ctd`UjEQD~NGXlZC*YG7n!o|I%{YT(L%qQa)s0-O-Raf@6nf*SbPmJy~|q@);_ z7$lm2Dphkc=qdZ?%Lt*>QBh)fd|7I;l~qt;d01+)O{oQZI0&l3JtsdYF$b;7Y*Lh( zm||$2VQFfRXq1#@WRz%RVQOYz30nG_m;$SkNi!T0V&+gyhL#W)K|%7?~y~nwc3HfhK3s0}nRVjOrakZYd}!g4f+$YjP^_m1T7;>m=TejpKKjfA z#0A|nVs2t;X_RVUkz{6)WCpsu0VDxQ^_lrrR!OC4X{kl2DOOfarD;K_i7BZ?2+87- z6p$M5A#)g_!6hk~c|nQg=z}Ae*H#%>8XK4-C0m#zCtH{pB$>D}fZPzDnHP_A&l||Y zR#spSgU^;ttw7jF6-OfS1ZemQ)0Jk4rUr=?MutX-<`(9O78sXnf%l<;f`clqK}k-S zjsYErXV^ z>_{@YBf%jCjn{(WWGkzroc!eM%)E5afw6h1MRp9i1;xn><@wpEc??>~`FX`9u+eCU zF2|x|&%8WXiiC)Pv|usGFF(((G$#jEkU-QDuiuU#AR{raBtO?Bu_TdvLmZ2eA#nh) zABS7xqx>u(=XpZ2dU1Smer|4lo}OM&DmVw|=>?^x=9LsB=9Tz@iU3ft0Io$y%KPSq z7KY}DmZqj=MxbK|jKI}2xK_ZP1Co+0&CQJr&65mMj8aoU$ERb>r!d!ASwUTEWrgZm zXe$FKm#>JJzCZ&0q6{g^@ zGc-YUjd@CHaY<1=sNPLUH84*!HBK~2voKFJ23;ou>pfx*5W}?8Gy@|`^Q6=i^CTk! zj3NPALx8WN2lpBw{wS_2wz2{_2wt9{x(MQQNbLn7Et2vp;?s&!LHA)Mn;Dy%rI{Nh zfzHdYOiFfTfJqW*I)S1k(IU;<(!c_AN1{QJF{lDWjb-Gbg=UclawA>xfQ6ZHnu)2Q zskwofiGitQ8lgO3l4fq2Y?x%8Y@BG8W|{)J*A;pD13U_#IhM>kKq!@`78OBLIVhm; zWZ7gR3rn+96O%N9RCDu0Gc)ue7!sK{hhCH8i%W{Etek^EOW4vO=@O4^DTWrtiN@w; z#wLlWrj|*D=(d3i{E`e%Qx9Im$4B|mqFrNPpQ7~xL6|BPzsuu&mN5|!*R)9{^PBkz#GEFl` zGe}G_HZin>9bBE9kqB|L1*C}$$`*Qh#U(|0dSLV5=3s_4sCvSC#~5ZD znqmez;~BZ+A<0naP``;qa;l-JrCFMVp{a=}=p0SBiJ*c2l7Qd`;un_`f%1=O0VolJ zhFFYEQ!EorlT6G_lT6K$OH}S8X=rGWmSSOMk!E6QWR#NV$^etJp*YxeAo&QFTR=W5wlDx!4InnSWPs!saCaZ( zXgxi*Jpa7pR8V3A4|#wUrsYHOj!KZ5N^WThSPWdiSb{x}p9j8r-7+;PIn~6(#0YfA zb&9bm)O#RV8;I+ul-JR#r$Ya&QO34mwBy>Moc;8YreY`N@enCOL^!l}3g>iB*-}sg>Y5 z2o#WzT$`Q>s#20u%|Pdrr6eX=fDUzq%vjLSMQMq}C5A@csg>vsF*Hp{Of)r4vM^3d zG&D{#2Tz?)!y%wZMk>##6U;`L`KI7zuAz~Szq6xHd~k@1YfzARe0+Lp3HYv4^At0~ z6thGNgVe<2q?DvIR|YjmoI)G`?loo>XI7=!F*xNH73G%)rKb6omcUC}6OcMXBV;+~ zEl4IQDaI+rNoK|tDWC(?QW1@Qh#oU&zXl${h9)INmGMbnTcBCr($XT?I5j!d$kfur z(A*-)l>t==G~gj6KKzI*-02U^*_ci+K(XE+Imsf;BFWS+)hsdD&^QfrHXi8KiM-5Y zkV&9~3=d(;;#8=Vkwv^psd2GLm>5|aS{kG#nwVG^gQjS3ukax~U_gCv*f4R5iKUToszH*mX`)%O z5$Gg)P^W_IFb5fp?|==9Bm>a!pFxs&YKpOOGH7vsd13+D2srKoHi(FI&>V{i=+q8S ztzw*FoRVm4Y;I{`kYomGrhz2L_grpjF4Ex|DJI6oNy%wO28m{-MyBSbpb>V+I4dM~ zkry$TLzG5|X-SD`iRLE8CZ-mKsj28aDu`{MJPK-af^sgYJ_J{d=EmSE5M&x?cns8b z1&xk@Xh`;TR&fQ@{NO4MF*zyO zDAB^y(#X)%l>s45qEjI!V?rtxD=To#VrAu+oSa%*3~5b+@+>%;fl?x7SXv~SnV6X* znwlminVBV-V~mi4d{07MN5D2q(CyzQ$ti~B=0>UDk#2B9k~B*gAf+*h0cFP!1n!1F zwNa8fV0{;e8IW4Qw-nlYfro$@q)lyT39%O?$(W^96r?7D>Ua~-WfQ3eNtQ{;X68wt zeltXh8qS0SD|T0+RCv%z32GRW zY;fiVRb%ksTa%*1yc9#rjKt!M+{A)-5HCMB9<;{>)SWXiOf)euwn$4fG%+**E&Rcv z4ixJkBT<%ant&97dtin^iFqmcxxpogC8?m6f{C|lWUyI$Jm?}g&{SEHWlFM%d76oZ zv7xDjIcTU5ECn(Zl6R3i3ucgJH)?x2G1Vf;!pPVn)zZw;6x2_Ix3)q31*GVK1Rc1E zqfu&RqN7l1W}<1QqoD~87NVP}po%-QBr`E5vkEkPY?f?f3_1ngJTc7>)N(;;`+&xv z%o0oTb2F0-4Iy-VPCj^AHqkUC(K02;*u=ox#4HW8=?h67WkT5m*%SjuuxU>D`8g&~ zlaeeflhYCnEKL(FEDX#HQo#0uA_X;`jWcr#j11CKL311_`ML3FnK?P}NtGq3#h_M7 zvYCN#N{W#M=w?i_B*^An>}D7mpy)JAGDu5FN=!~pu`ozXO)~@AZV9IhUP{|-~a-rZpexzbI52FwjL+ggL-)nxqn47hUOWdG+>-+mTZw`Vv%N^WB~3dLCO3X#YH4PYY@BSIXqjdKy2A}JY=+ZDumrk= zkOmrP_!{ggXxB*B0377to)QUuKQzR`Z)ig0FDKW{=)F{c)7`i|PA`O{+g82e$64>pK ztZM`*=HOXZ1vM*LWERJ#<`#e#AQ>7YSs0k4S{NIdnwx>UfiOu}@dR=qJeL`zrC5N% z%_X%s8P-FyOi8n}Oti2}OfyI{Pl4{FL(>IKydeL9hU6?v4U;U56U_`Q4HHd3OMgHE zdXVx4JP{6>@c>zaTnK|DGV?)A{=^hei2}3V!qUjnG$|=HCC$*-$j}I~GTzh(+*tsR z8eo`GTnV3B@htYu%quPS&r1avX=s#L9G?pk0Cn-ei#% z=NH2}C22{9}kg-cmw5* z(bEHmjCW>U3UUmBDhEBi4vVX|earKL$?GI)g% zC~1N8V9(Z;mPRJVDXGbZsRjlqDVC<_*&1AiAWh?AW;q<@8i3}GlT0kqlFZVK%q_5* zi)k3B2?v^iEzV3X#sztf#w2G4x-EzNav867#LZk7@DT0q!}Bh8H2`y zK{irsJknr^Wtyd_Sz@Y@sYQ~ZrKM>ifx!~OAqiQ}fqzY+A$Yt9GP?;XZIV+=lg-R6 zl8w_+%#6)UT^W$%K@B4?*UAb?L6=v8N&+;?OpzBrf@Z{w%QH(d;*l1fnpmV5q@)-a zTNqfT86<(mhEP<%3V(>X*p{NAC^t0EFf~m!F)&LsNis1tPBQ_`^kyU`XCvGOshLbb zf@UT8poOEL+j0|4la0(%4HJ!$EG>;wQTD@vo7)6xG9;6d&5V=H4N_AqO-vILL9_bs zg|rZx%^>r=@H!D3hI)FCp;kS;kbM90yi`a*gQEp&QIa2@4^jkbS6U>cnIxr{q#7Eh z85tOvxH7;b(L%1cq{tLfxRGKrbPfyIM$2S#O9MlLL_-U6<1_=%u2GncpfEOv*a7ua zF{lVh2i<;Kl$n=~rSQzpD@!dZ(bI$VEkKh)P&+|IsR?NK3)K5cGBr#}HAze~Gcrvw z1hr@5qx?XX7pR?_Uu0;8HuVO|6nc8e_)G^~*9!|@sB6JfG>VguwDW<9;&%DiN3!l`-S1_H8I%q>za%u^DRQ!Ok(^TB4ILK7SapwKgg zOs<81Ek+qRg!{!auOuJT3kwAq3c@C!`V!oVPccchOf@k|O*Kn10?iJ9BtY%sBG~d_ zum>Tl!oW2eq@)FfBU&Q>+KoswG)hcKF*i0ePcbt!F-`?d-j{<-1Xr%$h917wGGa8# zEH%~A+#n^bu}ot*VbBDfs&Gy6=Ycv#P^^O2YDWvh|D32UO?)>;SY&oc)-JwC^WI4C2jW* z6|km(NKyvoDJf|NDWHB>qLCr!EN(=R1CDagj1)LcLnaaPQp-WR;F2tpO^gi;Qd5#G zlM+G4k-@_bsVU`LT3nK!3yyA3P@$z0lX!?(78Zu!W|V1?k%_sbF{I^7%|;>WFu9pg zT4HiqqNTB+p@E^1u?fmBIXL#QG?Z|qGsq|`YCi^7PYIM_NJ#Z2iOGg(#);+@h6d(o zritdR4A4{$b|b9Oh09^kA`W%b4Rb2i&>S=iSd^NcSzMA@6rWO(Uj*veq^2gOnVY48 zZl1A7290@PsDd_HAdN7xhfF~Ziib=y#DixUKw$(*D)7FDp*e;cLt}LJCM74QS*BQ+ z8CzH+85x0wr%+UY3&sVAnU7D>qlNydg2hTtL=W0MIa!XXZa&yRo{YGvh8 zl3xTKkp(Avu(hDJHNJ+ZQL3ehrD?K}iG^`$YErTp?uIBh>WQ`%spXksVr*z^WNK<^ zYGG=Wl9CLuf<$ki&dZq?rX^dNC0eGLB_}5tfmWmxr9$TANOC1)2HO}kbPTGQz=aG_ zgVw-25p?dMVREu%ig^m?Dj---2%ARZV(=0NGn3S03v)|L6N|)TOAAX-%RaS)M9&tK z6d9Rp0@$FgL9&^FMY2h%iAj=~rGZ&0BuKHUh9^K!N-(sdVUlQJZVJsubO|!ltYK=9Xql3jm~3p4VgQ;wA)yyWQHX#_PSg?~&vGD8 zVTd%(l9ZBao?>X2Y-yQlnVbshwZSqPIMHI9nxC3tmSzf?n@O@TNlP(KL?0ReHGn{) z-=KwP@Z}^BLCPu()P#yT_nc^woMw<}nPy>TWNexYI@k<#u`aS3khU5@gwZkQ__qqEDVf5XI4FD)Os5DpO& zI7(Lo3sciH(Ac+GqM^BoDNRaO65NcMi19TJ%q>j~EfXzGEsTv)3@kwR{LrSfCC~9l zrEhYQiG{JTsikqUX=-Ys0j!CEHD5sliAeoqy9%iZVQOMvYG!F+YSwFL+6da40&`LWR#|qZVSIm+? z^YjKLiD?D~DHfnblh{gYG=~x&@}MFTQ8r*yVn%66pqmZMOw7}alP!(#mePdk3gR7w zn)xy74%5^$lhmZNv=k$wG&4&RjD4uIh$loqfwFW-Vm5f~g;{D^qN#b3rIA^RNs0+* z&1hnFDuoRy62b`)P`K7wn?PM^ZfRy_VUlWTlAM&5XlY^Q$^eppx|F)*3@D^X%E~Y| z8kw3UCmS1>8dxS9B_^gpR+2yxCn(H8=^iw)Vwsv`lwzEgYMz{Gmm=}41tN_S5%YK)L3T_hCZ?L1o0ytfSePYSrW)diY2!rGG;>pvL{l?Ui$qJ%-YzP{ zG#>8|4pYR=g*3xdL(s~!l%y0BBa38X(swS9kj+UBXV5W3poyjw!!+X*bI@iQW6LDa z;_vj-5@=2#BIYS}A!xt{>&}K0Q$u6(Bva7VwPesOOW^f7v~U>dI~~aIw4ni_9d2ld z)DAZ^4DkqZb##gM^mFq^+6ZB0Xkc!fW}1>_V3?Ma3|i|?{Blj=-N!)Pb)v*uOwPJd zQcTCz2{SZ@tyP5&l39RySD^Vz&?P-421!ZDNd_rt7KW)QiHVTS9Uw`Xq&oO=Bgp78 zBuT(}W#D24R6*iSHwK9Qn+eE~pcM~^iJ%R(7Re@Nh6Wa(2?dY@>X;owmk!)^B5X2A zE-flb%`1TpGK2PH86_r}Bqk=B7#W)xCxcERK~(}OjKGyLu8IdSFJ)?&n3Q5{U}BbR zo@SP0WQsaMk0VszBiqn{8Z(HmEGsipb5i2-@lolm|Mpz6DK{HhGxtTdRnV=P?hUUgeptJWIt;+)jf0?>v63uDV9%f!UQM9?P4G)q?ounbH))B|wk=4s}smMNxb28kAlhNhsK zqTqd$ zx4_EEzbGXYG#3nxcyJ^cC+8Lz`xm7c=VpQyBU&Vgxt(Lkhy3& zAgaw0lhZ5=EKSplQ$QEP8iLo3f&vEWGjnLl0L>v=rWhF~rJ9)~8m6YDq!_v~K;=Mz z0d@)UI$|SZ=xXES{M>@XqSW}*isTH?Dp=5Bfh41}6f+ZJvm_G(BLmPV2@bW8Wp@aN zBU@l-2wA8c3ffL)XokfaBTG}W)U@OzOS41+3lq>8?&yj@E(duQY_2Kvs1elDSxi9Z z+klD}1A|o1?76X_IjE%qS!4^+i=4&H3i30H^YcvHauUxK1PWq$ri?`pyN`^QjL>Mu}>`I z=cS^j5O9MXG&*Exo?&ERWM*k(V31;L2-4RImssG2F3rC1~xq?((U zBqgO-xH3Sb2-*axY!PiSD=YHd3kq1|9B7aZ%6yCFZ5XLz8rpL5i6{QmSc+sgY%x3FJI>Nbw3&gx6hWWvNA(Y2XUT+`s^|Fx1l2 z6x7s6b!C7^!L-BcMOOEonywMKsx}y5e89!Cs3i43p5Cgl1z+^ zj6i$B42=zqK`nD6d8B-YKXzdIKFvTAfM!W%<_5{h=HN{>uq+QM;xNax3{2CKj7-f^ zlG0Kv43j|%u|OSOg6HOe>jjV(!9^%2n86{19=?V~poA9>Y8K^}g4Qn^nj3&lqck=F z-CSv&V(Q9(tN>&nEW+_c8-}q4sYynLhNg+Bmc}V*rin?Y#==S;4QSy)xcC97fMy{p ztMJ6kl90^YRM4?t;EjcvL>5z2vybHR%s4I0%)}ryDKW{wGC2{{_kfmXRC5w>1s~O{ zLk@q@`tAS)m?Mh1Q*+ zWD#GQS+Y^GfuWIks-=;UnSn86wLARuKTu;P9qt~eE5S#fLe4#f_#3*y6lE15cokz> zTBe>J*zJ0H&;z#g^zu^6K}QayCWBTMf{urE4^goIEggg|jWtg)w@6MkPE0dQF-;>KxdnWkXd%%PSULspA{*7;hPni`sbY5@b#;jW+tA=EdhVGcJ8+Mb7Y zwLk_DY!@b`SfrRFo0)@ezfDbsw4Xp_52nLl>(~;Ll0nT#gES+PRAWn!%W_k5lQW87 zr3}1UF@uOhDNv8d(AdP-)HuZmbWkU_83`K$Kv$V&YHVR>kz`?!Vv%ZO32HpRR6@F& z7LeYgX9~!ZpwZ++(^Lx!6LSmjnJ=J4n2^0*&|v}aIZ1x`c~PlF`JrYe-~a=c{Wv?n zkh~3Yvw=ZcVv4bOT8d$!nT3ftycq)-l*VQ-ULee(;2tJfWmsmCWRo;g(ESvkCJIh-b+F5z zHBvwkjxDkX)~c4Nrlv`TX_lZ1(+x~Ol?F8G5w)rbC?|oojKJ1eCYhL8CM6lCB&H>& z7#kp!nW!!>0i_m#4oFQ)GzXnAYG7z#Y?KNbTfy#O2D}jfP9DUBzOeyl)GIkH%{VdD z1ibMdcj$wHz6dR#4GkcBQ$TepmaYCF;LRN1aT-uc!%UFi-A6=h_BRD@TnFucN;OM0 zvotd>H!?9uHUX7Kur3{B{}J@)Q?zXk;B*f*0=k_ZRHqZ%OAog+&D6BE-+%oELxER8V6ccJY; zaLNI#dN)ckO*6JgNlP)e1YPC_uP(ubETr*Zh}LF-jgY2Vq$Q`CCYz@jrI@4|fZ7qD zp;JiKPD)J9E-6Y(PPMXfPAn)XElP#E8rft#gP@k?<|#?$Mh2FqpbHXAj38TSz-0qA z!;$MpTq9SwG!xm~1ZyYNY)A(Kl5g-;zwkC@l8J#qvZ<-1sgZ?g8hBebL<$nFq*@QD zn<*MU1A7}wkVAY1N{5zkS#xM)n-`@P=jW7xYBED3150zzu)kTVu|-NUz?l?W7eOm0 z(3uzRAu5(&G0>qEpec2e)D$xVV-qvO6iY+1M9{i1P#p!5g#dsbFXuypNKl$Z-i z(-s!s9nhd$tEUGod^}51i$GT4b{XUl*^;8f%#va~JrFOkB)>>c52O>s0+nsBBWX>H zjf@SA6I0U63=Av{K?l2ljzJ~pw&Kd%q&I0qq zYjBc7PP9ld04igwtia=A;35Tj$rS}@0uKKbvG`n3`&7Vw7kJI>if=7zhS7 z=!k5{F$g5O1YGQ6R3YvmD)6ep(83_q+|0l{(ZW30%)~eeG{lhtyIKP&WKh#4^f=o5 z0<>xbv?~v}BC&+D_#nv_oL|5Px9I7CkFWt%YSgT2jgl?W%q$H|%nS{ajT2MB!!?Bc z0*+|<9kv4x`9upN0|QG7Q$w@FG*dG=wG2#+Q_~DBj4dq842(=u%pnI~LOPO|ySYKD zmq2Sw%`+@PL*=GPX%;D<0ES%m9$!!by$J?qG64szIV25$hrB>VfF8=FHQ>AkN(guh z*`oXc%$sX03=NYEjf~9K$rcvoCTYn=Mka

C1_lEL29PPT3=D#xv$7^gGcttF;*b&qoso3`%Hc2NM-*V7A*k@V92|V0 zsWhk|;PESojeMYH2uuhR!V(+#K+O@D5U6I8*r)(%mY@oOc>IA#mVmZ=fh|F{*%o}7 zG>W}YA!K`@Ldf=l<`*P3@*&#`5#k0ni)27~pRUa!{#a1_fqc!tzyLY|C43$SA3t)m z1c0Jt0lNf0C~~0+89?Eu{f0vxN4)YcdIpMQh-%P*BHD}CIRpaUA_@Uea}TuHoq<6h z38_~HsvW}TbBLp?RRyoooX??)68WIoGkhfn3rZw_1koZNWFlJRL(&6E{g26}-662mBvz=G%n5=aoaK;o~RhKM(CF9>}o8Z1ct zt`Vp=kKQ#h1)Zo$jiG1;Vbx?7hPGrDhHxV`Mq%G%7KS$8>8^M`z#Dn51^bo4_Fx3AF?on z?_pL3hS6ln8~S&)I( zn*&j@f~KIkj<7RmUibu3#K3SL+yHo?%Fdvo#?HWXTqq%*kzs-eGXvKN79mh^c|nAk zp`Sw;+$j_gWrhtxJ3yGAQn3KS1eJyhAWTpxcmcu$m3|CjAZ_5%FZVOVT2Ltg>lC7h zL57dOV|~R?wV*a6bPyU@2&NrH45A%mhs6e{cF@ozW4NsvI|B%Fo#!#V#=@`>DhUcD zt_%F&ThEZiY*xZTZUvgJcX3 zQe$TTVXhCnb(5JGkR`c3axh36KvhF_mnNvOGk`GHSDvoPObm`tNzfWlu0I?+oEeM^ z$xt!Sg5P!{97)%V=x5#2p-||`@9fS&MiMK0( zbb#;P03`sh7Co@@p5;LnB5<`bFtAFqFffQWXfQBrk!EDz_GIK_=75$b{F)4qlIGGY zW(ICAMo#7cs7$&h0|Q%cePjr$}gv&IBSv6c#&&6Jqp z%L<;3<37TuDex2Q8HTCguCApzJ4EIf@6!fWhFGX1Xn_p(aRG37vO%7if%|wExIB3v z4=NbIgG>SnU?!+Maey#EEsQZ9pzhP{bew0=PW63ss9$ zo*)as$`ceZh;{}Bp75(I40699eg&m6?(@O|Jl7FoAP3l7V-#cndt3*q64X%Sz97Q` zDoFF8VxW-Zz9Py8YIfE`#UM@sw|LNmKmmd#1PYiIs76rYf;M+ig}^Qqy2HiDz@W&? zz%J3B`2sY7(g)fPP5k(Ao^C^ll2~cL>Ik}ycf$2Bco#0(M+-G#a?Q>)?`!`JD z;DF+TDunnK98hRNpnyUX0tJ*XR3oT-fCdz*5X1ot46H}A85r1ZXfrSIBct$}09J;{f(#7Yv5c@oia=c9tUzeyNMdAUkUQ`P zV!jf1z)x3$one&*I|Fw%BbVztW`+PIW(MwjMpdp)_gNSkK%68-9HBJ zm?Z`J-mo&oF?>vJm`KI+!4+j&y_#Pk2Mk11%wf z4^JQqA`egSf7s5-kPX!V8YAS+XO!lDj3x*^qxu@U;0h)d{xuP(+U-HtbxC+WiC8%-D zU+vDuum-9WRLgM}F!Bh5v9Y2En(P4WB$oqCoPr_?bTl1f_+|}uhItB{4BW+#q)`BJ zNeLqlIB6^ZabQW~fihCk;Q0qKf`I`gX{bO2QIkdnR1_s?6hH-$k_MU(C~1^HRe^jD zO&X{|;G}^p1Wy_;L6oFX2{jKTX&?(CCk@cObx%Qg^P@JXk^|pX^XC@mUNeqU{%j0U zpe_u!X65_;eq(t`rkQfKW1uP|^h#^vwm%RzU@v*<7|u&L^RLmqS>#H;n-h5|Ui;VmuDtP5kffHt&r`_9O3xC7DW09SZF z7^V1C&;-H3_#gsRB`D|eUq%uHxgH#hqQ?*_!GhpmL>2`lZCEg(jg$3hL-W^fkgw)@ zF)*-h0ueuT7#PGEbr~2|xiT~GE@kB6djJVvh6i@upur&XXABG=MZB{a86-}IF+til zuNfI6w?AiK02##noKfcEa|Q-b>y6u#kcVPXt#RAXlVVQzbHEqVni2|7}S z+kpkqON<0p5tG%}84jtjGjQh#eT-mcFc1UP5bRvRUziyZ#F!Zv?=Z1&xqo41m;jP6 zV`LFZ@MdN>0OGu65eoKZX80h+%)tAegG=`dGlPOSGXpOxqrA`sS7wF)5NEvrzl=91 zFG4&CDrtE^7t~&Qf*95T3xY1F<-hBSPztKxd1o?8@M|LJ2MexX6y-m)1rax3L48IZ z{=H~|;P}?%LRAThZvnp}sDiLagt(G{LFkJ#BSV8YGXr-ChtNY~CWZ~-%nUsS96X>4 zjbVyFX`>XtKx zpH^dM_zg-0^T4SX;Wz+*8 z_imO3=hF`$HZ+D5q!2L_0Aj=PX#1yr%cm%U@EE!WRSKHRfag;bL0CRT z5rpMaWI=d7MG*wGs6hD?Sr93o3Rrz%W(a^f0#t19vN3|%S3-qcj0_v3m>IaYaR@!V z%EItKikX3J4wvAkYb*=`(##By?73$T3qw0pH^{eai=~9rZm=*oNHa6AEtcZi&ceu0 zAkEAG+I+zyxr>!y6I3y%c41q}&o8^02}KMvhryq`6IG%9BG5IyOR%V1xENJX--MZi z|I8jV?K;dH0{hVfH9N&XjZ4rCQ6P7TKxYy`%?!5XVqkY6i^1K6EC_cOvLIL`$X&>S zaCe~y!rX-{h~zHN^rHdPU7&UY+Zqn=juK=siH+dp5oki7)*qS>sAGUC1aAEy3&C4| zFhNjyfOiZ`p)LV6h2R|nWI^QCAFusC76u2X4rs#Q`E-?qAsi|OD)rgcu?YOXhAL)L z4%&+%zw#do!vbk$2JT(LJR3H!GPFXqfeLZ%JtF)|(8S~v898`opbH-1W?&F{Y{STK z0c6x+LGZQk3^L3N@d_+L&3?=b1|T-{TKEJRXn8xwm6>4zh_haR>$4X#!vPR`I-`&v z=sNfhAPy+^g3Ase;r}cQ3bM=$+(-Di99}asB*-!|fTmG|mP9Z!OaM#h@Uw?AGdO++ zYXDD&F@9&^5McVj3Ks-b;*7spI0Tm8V_|TGssx2INRZ#_1q(v}R1oBUuwaotGeZzm z5TqTH!T5izXF?HV{LP{zux>S~pxFc_(4aj7*OnJ73hAYV~Jk_yBVJ2}T+4 zM6iM!SjHYa5gZ`LjF<>+kYh$n1aAOwpcBC$AMzI6W?@K&It&!?+(-C@_5> zaNm>To?*_)pa32~;@kU*m4WLANEhVFcWB)!4iy6R{b6-4iWsEs=gS{9Sc&=_> zWq1iqpP=G{d!HEpNi;EV`rL~y2uhzk2i~$W@IwO(lqfOoCoY4s7DOpHYTk12??w}Ih-4Jv zmq9v`2c*!Ui;;t$A59SKAg)kQ+CdlzZbu6ujXQ(1BRPnHfpvofGXv)a=ztg~xERB` z)!7;LsD-_BwN)Vif1%kzr&=P-SM|KF!D>#A?FCFhP}>p+|~?=jc@yhB~NfP<_#B z%p-8)8mgF0D4Q%~!sIJdC1}X9*N6w)-?n0e*ahyb>w#|EkgR8AmRWJMWfTwq z3FbqUf(36f3h+zFFfz141wj=U_gzK~9zhc(6u}-B4xaL>EO0SU)3`T}L!jUqsu;N1 z20Ljg)JRb4sW+bk8d3|PqM(HU+)o)L89)=ipj^e!4jzfTsKw3z8b{;)$cR#Of|fyW ze?lud!GdT-CrAulbb?YlyyygRph+KWAp-*gO43IWgeQHND9FR`q>my9OZq5+u%wSH z2u@+3q>my9N@0FTV>%#iVi*I{P>VQzNFNzlTeTkIJa z7(kO>+&oMy3=+I?tPJLHtPI@W7#Udgbr={}jX{K!4g-U=gAN13mvA-)o%L0gn$7XgRcP_0}l@)+ha#&m^64Emq8-bfQ?};8y5qQ z6=>ixu8NUCr1d&0LxC>?10$p8c~Gq^ddr7_fl-v*hk=1fl+TBOfmu}4hk=1bRMLlm zfmKx2hk=1jRMCflfn8MHhk=1ZRLzHhfm2k+hk=1h)YylCfm_tdhk=1d)WL^=fmhVU zhk=1l)We5?fnU_uhk-#rG}MQIK~OZ#hk-#zG~I`RL0GiFhk-#vw9XhDi;q72BB@Skv{{ z7$nN|*}y};v(p$6W45!vBR+Nc>p!4G?w!h~0?lSWs}z&P5dib*5(Laqxg02T24BV6jaiJZm`+js%Nk z2=Ml!iSalHGBBXT1*jc9d$%fziJ+Or*?T02HW4L!K!f1$@M!?~Yp(=2tTuqy`ylKG zAod{jzJpshN2w@)pu`fZ`A3*HO5VpcEW`@~UAnX7T`>F&=F!3@lFwDM& z>OgRoggLMQr0I?XO0onkf1P~`RSXj70O@(jA!K-&mEi%1{gqn?G)gG&o0%c- z1BW1JaM0m5GXrGsjR&L*nhqF1?abLckaI|FM|12Y47hK@Cwi<5!%3rGSq zhXtPKnrsf8=z>}Jzz4JxNn))!6T=^MCI;S_jM5u8KqfMPH`_CY_v^Alr@(|5?U@-C z=rA+zt{33f@?vJ#2vrDgbi_VjK@|iwIzTqTCksJqtQf0L9Qaf6*Ah3Ajh!B9_9oG9_vwK1_su~H_Qxd z+9nJPGA!Vk>-1}^3@i*X{c9N*N~W_j7&J06%CxRyV7O(>z+kX|kx^#O24?Ug6N5}f zMj5V+%nU9YnHdZwBHbDRS7r8(-gW?Uz%q2(J7;fxlW-!>mi0P?~jPj&-3Lfbok1~coILgNG1Qfa_8PQ~9 zj8qstG&4V$9R~=!4lq-`Yy*DyL zN|l|Al8qaog-Qb>XkHiWiYl;o*BxPllrT$qL0KIv1G;;Jfq}tbrHlMC5f%o4-;4|f ztE}WfA>#mMYw&`S4@@nnel%F&f+#s4#RNzzJRO4CF^u6GL|G6H2DQ`}!=D{tV~{_} z#$fOi-ady&s2^oxNB~LvmYlbn8B#c$fv&WZIl2~d(Vsy&6QhjUIw&WPiBaa=PADgm ziBZOX7nGBLbmBFrif0V}atv}doB=oZ>O!zzK!Y%h;S-NTWdsp2ps4`HaQzc(48lGf z3;(J9wLvK@gLw z0H_#-2tiX{29qp5sMLiCf{JE?3?>bJjND3MDa^os2gw1TW`IE;ld1ryynxvSa-TsI zlQci5cz_9l9T3H&hUowjiwmUjpwWMlZgfQ`YR8j)^63kDg(|DI%H za1G~RFla*Dgax@aH2xG+rjv>P=_OD#0tpaMC1udf#E;U91qq@zV?kn&W-Ov$fMk{y zNfri~hE2>2-#0Nc7#J`kZUlllN|J>^0Hg?FBV_H2fhF=KETD@`7{ecxh_MqGK<0ZxbqPq9H9=)uhYK@yY;AkG0b z4h?*m75PE7Lxdpv1`OhvIZ&JpRta(q0~ePR3qt{@==5e*f^x-y1?^zmcV8)3{Gn6(j!6$d}a6)@+6!wqzq0LV2cf?!v{6Fz9o8e=#p zH25)U$arjyBC(1jB~0NB$iLuqbM>d8(N)OI|7Pih(nN|Z;b$l zKs-__7F1rBiZb%?mm>-KGB7ZJ1bMR21$8)tbQ@V16d0HoN)-fnZz35Fvb|JCO(^LD z3qt@%UPq0~u91bIfq{uZ=^zt7Z!?l%pz5ep2XuF}Q1u5Eh7BO~irn(~A6OV3fYe_B z)us##{H(7*#zGT)WddzSXJFv@j$}jz)B#~gCV=e1NE)Da4kBrAf%2dLBNKx`CrL>n z@*X1GL5*9GcK$=`sDf&X*#!i)p$Qu81#J}&IEEDapn+3^9<rQ#H30hI%&78*=t=0>Oo6)BA2w_)m+F!S$5TF?r1^-^Xo@OlzR0%BkQ391`% zXo1(0zyv{^0myn12AQ40EDU8LEDQ#YOo%gnYr(Z^moa1{#z38cRbeMHgRsd?$kEIO zY0QiakRp$PfuROGzV3ROjRB;{Aeos<>fLPFC^reqR7JF z@RyOnLY9$70Mt8yc>@$w2I-(3xC{*ZtzjT8L<%&Dr}l=Og|AZ*RZwE1!fAQ%zzC z;qn2cv44yVYOmNixC|b$GB|)Z@1a4_0OCY(D1n1w!#_w+@PmQ^>{tfKtRZOWmjEb8 zkOe_O!@$76FOHOaK$%f3kwcoF3r$cnosk12WrI{|Dsl6nq->BNBxN(8cm~weR!ig% z;Ie%P+AzV$pccm=z;)pTD}%v*Mh3Odj2v8tU$8P1{AXlve8$MfzwHGg3P1ypj@?YM z{KiOQP@v&n$AyeM0%~Z2I{w_U3#~7O3Os*vll#pN%G%;mXLt!*-;x3jXkt3G+@R*X zz@%)1sbI(UGRX?IzF=iI01A$YOfvi~NR0u|h^gZgCK2AZXoAu^L>c&zn+>3zx#Kh@ z3ErPbDnWwK^9KaIK#>6o|6~pUl&k=1t*fPRpk@V-AX-*{2th-NAEPkLW@bl8GN3@m zQy4NZu!06o+2$d}-dwJO$5ek^VP??3%FLh;#lk3Kag~{&1jMOeVU(GFotfdrb!G;I z9u`I!*&ECZ;WwBW6i%=*%B0+2W>^T~EN5ku*?5DQ;W>!2jD=A~`zA9(=1pb>g&bDI z4Vj?2hcUcO7isJaa?tyARTc*Rbfl;QRY(f4EIj!n?`2=Jr$Yih>0|P@R0|SFXB?|}THt~N%>eGQ@8)Phx9a44%34;1`hG=5YJ{{bt z3&BqPi0;%DkW+i9?o?1kr_js7f#OtsecA)Niv3uX|gb&s02-~DI8$oL{zMx9n*~A z>#wpg{JhG>pzw`F5M=TKklo*r)q%$47{kS`u`xtmV`EUzXGJj=lqnPpSdq+ysL#K~ z#;^&bJ`9Wca8^#l*gYs0GKQFZjlrF(6W7=n z*srrODD<=PBL@~J5hzSxJOu07{w#Wl~THh+;pe6$%nWDQiH2==OueVD^JD7|beA5vnkiRREC* zK?^S#!$E})355=*DGxRhR0uGJgEAm|@M8wJ^p(8M#^4LepAT5MK>6zesC0SA%7>CI z$Vn0mTvmoG3asA%OnJ`CuohIO zer9EqQF{rQlTeUhVPs(2W&*xI95j`73o?}!a~)cPPh(`1c>=4!S1~fme1z5D`7D*#fJy-!LIY6+xq0jNu@Ukm3Qj`$07VWB6HJq$xB=L@dJ;+Kk*XfUJDo zp~u1?0MZSPLIwr~aKN2l>upmf3*e(D?>1M7J<28D-=DC$A>D?DQ4V~{Di#mung z7BhpwanKavZDxiKx0x9fE-^96{JqW0V0MR@L16_Gqm0uXW`=qYr-KPmkTZh|a^drA zkQR;}lOV`p4;Yyk6ik>n`H$*>k_W_zU<=Ke1o&%rf(kMu!OKh>0_ldRDnYxH7#IX^ z>9Q~gFflPGSTG3)TtYGp)Dl&&W)cv%k1lA#B!IZf2h{y$4EH?G#!wFmgFwVcHAJTK zJR8F$kW3+Bh6N%6G8a_mfZYj+YmogYVF(WMl}sEc{sb2fE19%V{0SCB_a{gY;!ng_ zHpKD+$Po@n7NDRc#Yni@L01znhJynVBfY$3LP{^7@{Te5-FY?!y$fs%3T(`ner0DC z6(~W9DNsK|0dzJjyyO5iI~l_rFR(Gxf(((tVu&oWs36FY08qTjfrix=+=u2_N7$hk zGQO3NIW~nXW=0wAtx!%Z8>7tjTg>3e1%(P`Mw$LQ%;32*g+FYJp#BUvwm?I!jNv^O zAoF1gYRqsM(Bd7&@TV6b6K)E22pN!17{lW(LS=#x*RH(;4|rX_2sJGVi;v=%MY*0! zGcYuOe3Zb&{>oiCvJlU(T|k_B?gjD+y>wNoxlQ$2*~Z<6F_V#93#gS z$j{1ffQ5;{dp;}V1`Y;RCfMO;1`sCb$e9EP6Li$f1PBv!SkVCp6LeV72QU+SSP?kQ zgAUYTU|@jlM?(>VY@Y%jRs<6Vg$i`{6|xYzc91f}wn^|-I|Vi-28Cp18}ON{0c=c& zt#%C{4tT4bpj;d)!v>JT1ZECI*#;`_7{h%Hk*YjU`_QiZZuOsf)G<=Hr{1sa4TR04G%KPaNJ{N zumf@CAE8`$8T?%vQdMqy8h|i@0pjIC(T?z^;a3TaT7{ft9 z1`Z+z+FA^+6hP6&7_M;za@m~1enim^N>_~G+pa(>g{#c`v(aPgI+iE|_ogt~ub?Po zK(tIi%^}8cQZ1Bqc&VIaYgS%slo8UdPbMn4(V5)`RuRT?O`NUbcv zRhj`mQk7P~53kY|K$xH^?E-`es?rz)zzRTBngN6fs?rj`OtPyqP|F0ZN&^X@S7~4| zNR7zAz#!OtfR$l_0270P1DggU1A#b>Yzkm+1qdQ}Yl0xy)u6+pFF=?eZwUy2L{TbN zP(vEcB_JVmmw?4UF5w1+NdVX!@I|N%5GE*0Hh`HZVFH?xMK=m81~Lkgl}O0^;Ny!Q z2qCpU6@)>}9!UE$Kp3h0*&vM6{@fsp)c$-RjA(xfh#<8;9Y7rP%nNE%pl4o?7)Is= z369Ln3vPcFh=3vj(*9fkVpAcrG6;a0W{_0EzyMm4rQpb>fFcOmPz|agWLnQgtN2395L&r`$R~n4s#Y0Kx>-C<`D=P%V1_!UWYQ3|t^>C^ZVGv4viv zfW;s+3QFAuTFi@9w}FJvwS&bV+8G#7=d!#&#f1l(5?AU0Rt5tuCI$r%C*lArLjo71 zH_7D)QZu13i;w5P@) zm~w##G#3Pbasexw2Dx>?_R(B040AyYsDJXCHhq?X%LU+Ke*!4Mvyh+QM?2{#w-yeQ z#t$t!=_m;vG{1z=_<^)xXxxDa0`;(2*)&i(5MV(THVyRY*eF95#H19co5C2bb_24p zQz3;7G$jY_qJY*dF@|dfvoN6S+XQ)1A%~3vW#1-95VV*LbZQA$GXrGa3%Zz1#{3C% z1xGd;VkHq|6>akk=(KMU8~=2qbwVH`6-wFU_(3i}HWM^qOR>xpqF(@n9!GaLu`2QlUF9$EV zT*Jn}zXU0vfCK{@89DfuA`KgX1pQyL@bJ$-6V#j`#=-UJA}d3J7!!l0zPO;g%}!Q^ z31UnPAy>E=1U4cW3>pv6G!O>`BqTM10~<1TeBl;!?sy{`D4~L7AXzEuHe}bo!ZtSe z(ge`bHOBClx1lq?r`QBQs~EwW7$EyV6i%}V@*hH)Y6c|-g)?j%{9Dlk!GW~`T@Vsj za0@{b#EjwTcc2zNVB^1orbFQ&n>hb@G(jCL0RaIFv%x34pezCdtxr>U$cD7g1LAkk z8ZWpTK*bMZ_yw37B-sT)+Z_&oQie1;2ciUmxYhSA)U9Uh@BjrB+>GHTU@}4M@RJ=N zd%vDT_kJm8!6i0>m$BNJKsJRb)G@HlxX#QV!lJYx9Q~+|E8)mTeLRQoAV+Gi1}&9&LF-PzzF=SgpDv~_PY!V+9jNio7~X!C4YJ{7D-XsZ{2i!1 zYX$iXeGNWnaUe#X2ML0cF{tQ(IRGpONyaFPr$NaBWAQdf5Zz}WF__Ob>w>Z=#Apyp zVLOt~Y`}50?<{l$>vzfjcW&^u)q@}K~Tx-+QArra`mwT0|SF=2V)>gvlAqUcJ(pHyJ%M*gO-4z z-9HTx0^dJeH( zCK}Se~eC5E*!S&CCkzqk96NBqCMoDlv@&Lr~q1aWR z>qtSK1YywfHCJB_%(BIYgF)aQQo02fF4Gvjkh&c?;J_Aq&c^UghlN4clbOE`DX>99 zmAYPNfejLLojo9dtqcxq|L1HBpFpk+X6EXaVPp_UV`9(^fd+O!8WV%-d}v@#0CDIT z*w>K)8`Oh!ozEDE6xg6TlQF#GIU7T&Gz)`n8neI*q`(Gc8r^heq>>sm_r@50^f?Q4G7&C%pCmQpc(*_5EvL3K!UC-7&-Wna{_4hL76)vrHM}9aSq6h z5Su{d0LV-b4}3PKjQmQ-W(nOLNXwP8!0Ail2^&L_H!Fj#H530oLqxufXJBB^wPE7m z-+<%=kf19oBL`0%nqb*24xU>`)i3Bwp|Z|lpjXKtF=&;145>;6 zRbyqBK@~Hi3kzunH9lcu__&6RLD!3kpW6uG1yCI6dNXnGBfAXLnRn%5BOT_fg20T)kug99=q_O9MIY~KPeM8@Fam4@ ziz%dos2j(?mL>@5(y@bDwE2dh7A?GU*T(@`1b62l8^d-%76#o#LXC!u3=UyT47!Us zc?llERd|yB69{I(8_;>2quQI25}+Kg8zUB(5f29@}`Ce zq~%Q;B9N9hJ%~VB-Xsu-Sl;9iiL|__0L1BH%1=07Iff5?2i;Wmi7U+XUDA6aQ!D493n#i$9C}SNngFzG% zLs^@MQ1f&~hJq+22A0#JT>qytGHi%qVo(%fl;i?6R6c+>u@pB@1V7}kGAKkdF(}4@ z7vN!Z%*$ru=$P~BA$8$FHEr1(ah^L!N9TY#^JR0yc^U6RQa6YMiTU5)WDrWRWnu`3 zW@0E?ssL&5gV^5CHt2(BCI(mfwLux=_a?D22*iM0Bee1h3xflM&Gp@riJ>8ei9u-% zqX;+yZU9LvlL2SI2Qi2YC=iR30UctIGGIY0QU+WQi^zZ%Vv#Z+LmU$WXy*(#0~&xh z&5Y(GO&w!wv(<*2g zkSv=Rgih^ZWjGMW#85U}imPK6E5nC4CIy`$m`-XT#y5A6wl+$HJhyS*X{Lk>LPH)fNseE>lK^4m7XKf zpz;8mnZm%mmf%Nh44dRx7<3=<%tGqRgNCVfA93&(BK1f>Vy=p4qYN^+(PNS>uL{<;!-M2huW7w<6 z!k{~kk^ef9deE7_y7L)1_`8t88zkt;h&FTr5-htnAVVjR(Z%hL*%+FXSQvD-F!Db^ z@&c&p)7{F*!9NYj3m`#PO-9u5U65dzEj@=E5yL-_F~?7j*%;bDemKd<_0f=#As`7f zp~T3+wb+)4VFD;e>M}w`doLt0F_bw%8-D_zStI&2{shnmAj=#XK?76_3=$#h85pY8 zGcXv&GD;L|U}l)MftkTDl94s?Ff#+2_Z4;q2@dvMnhXpgNo))p4D8p>voI|3U}NAA zkVrTTIV7c2h*4tRQD%m_N0}K)r5Ppok1;d&9AjoE)nJsUJkHFp?l?0;sTiX~$|+`s z#it-@uAX9M5IPM}lY5StVeL6)hSL9x63@;vGpJsGsENM7%rFDQ)?kzfdxPu#Z zvoDz$N}U)btX?rQRK9|!pZlu_d1D`p14*USv1>5LNXubCN+f!NZF5>H<< zGbn-B^^6hzKojS;*EiUVH-PWti|~r z8$&rr$0VWCWh@K<;!F&slR1PenphYbKx_p8A^CC^h65n>WJMw2aux;#2_}ZpZV@5& zau$XKAa(yp~qz`3>RdX7)nz( zguXShFeE5I)L$=SVc4Jq5?7Q59j_gr!o*PO%*`Ma^NEEaK@}{)CEUcqV4%(fzF1qH zsfmSwK@%ha8Uu!SNWSPM3&RC1h||o=Sr`nonHWmjC55!hSr{gO*nS*BUQH|vA3$sc z0U?mJ1v*R&rGDyQYd?V4&Y*pY3_PF{ry#~MFn|uMFYV(cR*9VVi1=Opftlgi z2S{9menh01DIb{`R=;OvD4m9gmsaqc<;8n!4A(z0GnC#DI$XiR;9$YTPoC14Jqi1|Ofyf1iyZ9VGKa z=xP-Ug99i8UUCSXtYTqk0I|(Dgqm7d7!H8g3IaU6s}Ugsa&f7-2(M2IXe0`3DY$!J z@q&ec7j#5DLM zv7vbhG?!5dO%e=tprnE`X)!PeEnUsZ5CBqTsVbDx!oo1Y4q^kSP`LmSf35*8R0Qln zg$JZi2>`Jb1i*#L0uUQosC)pip@qr=2T)M}DO4^vA{8o5AaQ7+;*3Sf+`W&=shJiuo@h29B3n0}mIrs~Zf*7P6bQm+nl|iMi zc~B1R2Prf_KeQhth<=tlNK6O3A`BE`68wLd8T|f05>D?QW`^S+wgIC=^FL;WL;oP+ zO#hh~Z2qGrD3BW&!-MX#F$n%;W+;6x#8=D0z~IKjQ2LWYh^dx^!2!fJ;}DwF!otu1 zVk-y;?On~vZ~(+M7ZJJ!%7X5Y7*nfdVMuUiVkrH>BP3PJ!mt6vHscWL1KHvMiGhu) zSs5HaY;zHzji6%71ELpH#4G@bf7A!3rwbr9G(9nRLiB>lW(N=(cM-$Dz;hBQxr18T zr538ZdqFM$1r%t~oq++Af*FLau4ZLu02ybgDs-}ig<*py!YL0x;-4W-5%2;fVMwXz z0AfRvb^(Zu&nXN%F`q&41hE*D2}>2Zc>~bIl%N+v2)$j+%CG=rrlqRTyA~FP4_*-W zfHH!EHxonYcSuH<0AfRH*9RarG$T0pfHDZAc3l8sLo>nxUr_Sp0N1Vy{18Fe;13dq zWCVo(q>L~D#DQi6hd`u^@F5VS11TfSM9K)DvHH^Q8Vn3V4?!LYVqz$@P!)RA!oqMN z2;vb?95VznF_eCTlot|gEDVeJA>z0BSr{Y)z~Yd*9c3mRW@g|%!pu-A!pO)9Vh9%;fgGY= zD$2+x>~a#yQD9^g-ggelab#o^4r5~hUu9d$#mFdZ#|`CZGcpQW@Ig6dp!5CMk1{hz zh#qBTC;+u!br@NX3NbK9FdT;qNE|!O%wPs;y6P}WY`Vb~^Rh6QfjpM7H=a7hLR zR!~b=Lhm(HsDY7z71X+wXnO+{nas$*3Tg>U6uyIs_%SlDg4$rL``?1TX?=mv5 zg4$FP)!(5aPZ=eC{eZHcF-n~N31vTLl(_l}%6`en!0PjZnL#4_2Q$O^AIuD;W{eW| z|1vX}gX&x}MhRsG7KUmD7LYgtE2xTvWI5HpPy;?PN*rT=X3|aGS`lKPRehlAUwHo`X$Ea-E`10( zJ&S?I9?1ZZV$j7vNk}dNjXq)A{sOLo!B;aeAf5bP0uCI|byAq2f_{AzXgw&#^-*9! z9S$DQ^-&NnK`s!(ag7uY=o%@QI;h*h*GWwXVZwKv6!BRe$#XSe&w*}mLh&4E@DFrj z6Bp>lrURf@R$$~40^QW~0mPx}4Nag`8{pu8xRvJtG?i+hrc$tzwHf)i<~Fb}D1BTonX(d6C2*(XYl+j&!9EI8EuO@#i6_Bb;{NPTm6! z-fozaO;Mc;8aFOAW8~x7+Q7ol0CJ@nBcIU91{Q`5AdU_P&vqnhL5pfZNk$4u4Cdtb zNKOVV76v8vDkOD;oeWxy2M*;*@Df7MEm0^b0_;{tMn10nWh@L2K+bby zT;7Fc6xvmQX&_b^Lk6K$U$QZLzRk=~K97+r-IS5xKpGQ6`F!ZOGebHPgR2M=WR!4* zWn{Pj3T$yE$n=#!CKH3JCA1g`0CDIT*yz(&u9i%ZNP%4nZf?AN#m4XlSlCjOU5t_6o} zCbqDR8W7h)b`X4i&Bh>O%EC~-m5Kiwl50VYsPb*t7P(h2QGW+P1(Og`j)H8@PI|AM1Z!XbZ6xJgFTlUp3n2ztanC!QQAl9MBSa@3EGWgu z!@#eEq#d+Jh<7HV1pfvk{b0crjH3Kgw}ZMw5R1Tq`iwmM9cY5~E0|dLZ-k?&wAW$c z5ZHw@BM8ziF&oq-h8PFl#KZc{f`LJT@i1f%rPYoRv6u<8%$_lvQG%TTq^8xD=Z7D{ zgP_h^s~rc=Ycw(5_ZCKC()B6LAeRDME=N|1I?RPsMX5(LGA#4JVuf#XQ=3l=P4 zVqriL1+8s{$2!mPO9;n;WLoWc_;;a+IYcrFp*R+#&;ispLUt@j5S;Fi9Sar&r8{JI zfdt_$0H+Jq_Mg6P>0BnS=;AVr>dD4K2|d^q<&?T6sloj5Z%SZkb0d_!n}rwA-aZ%A@wq&v;gRaLQEqH3)FU zJ!WMP*uub|mdnJ$54x^)22?RJClHb z9Fia?>pC7~QsTFG#maC3suEPfI38mX;8jNxgx-@Q1UiKvU<(6-V6%)M==6bxEes6c z-9tjHPgxi?fMi;P?Kk_BHj3=;%ZzMy-F!IusHg{lPQbNFS$$b#_8hEW6|mkoo1 zfd%Sga4^7b0_KDYf`b-z6ELzMIA}pP0c#||gN{K=>WE}kar zL<*7vAPzW4xIj19e1Itg-CU!vivh9kCIG~N?muYQg;cC=*o7!oAueTL0C@mbtRjm+ za|+6Fe4w2m==%>qVruC755R)x`wu{Z@D*PIbAQ5YV_*Qu2>xUg-~vVNgIx>^g1;Em zxImFAup21=96+2U+#C!H-~cGt4OIvVfDOBm0^kFP0}TL$JxBo%uxHQ&0O*ML2lrVS z8ule z@t^^3P>GCkQ#?qJrZ>e;M7k6nRET2S6b}+4`KI^^Y&XR}dB)0c02Cas`T>@ZKs`6= z+!QY`F%%KXparjLGgx@|C!(!&RGZEsCeVr|2w4EhA8;3804VSUUo%Rh7yuFk-wls! z07wvOz!DCG0icGW;2TC3KJkArzcMg@1SK}|)*=;IgwG}>@OqilS&YIS_n>TXW~64f3AkoCUJlveo;s69ehH$q1I_oQ&f?&yMXF)I zVrw|~W08Ce5)<6V$RQw!R6&4@QTxX!FE9@&a6z+`Y7AVUtv*7_U$ZcL*vG(-I*WtL zUNJ2ws{x=4O3Huos^cQdlrN3ciIIy3A!MKn^K=L*syg;*7 z>Teij8FP1? z3Ma5D0uCTt(Qp84Ha|932%Nf(Fd5`DwQ_cK2I{zN1H?U`jpwH~Gcg?8%*2qojuGBW z03Alb7_MKz29cS=!@Ud?3y?$vTKS$jmxHGZDKf!g>pA#+kwO#H8WB9m$RQw)R3m~K zuWH;}pqWENa|Og>44+d08Hq_<3<^fDU7&5njNzb^Bm_!24?wP7!@&hgG6Dw~7>G+M zU@IX>1>_{K!4MnY!ff0MpW~Yc9xu+QggQl-Q2_ba9AgFshE#Dz0Zc(rMis>AWFTu8 z!}nK0WSM?7M}vRY4QQY0j*32HM0~hz*=kI zDm(zh&E}H=SK$*toP}H*Tp)!PK%6Dq90JJgFi?R7x?u%$YYW?cgik@%Yi96?FmTDe zVPO!s2o1x?H3%i3o(9N;0zVg`3R=iAf{aBP?*v_p&KO=&4b2Oy82J|?C45jhl)9Rc zgU9eZ!XS{K;5H5cVI)U^PSpsLVFooQ;UkccRQS9an%FiX(n&Hno!HetWw@F6*CSa9 zy5lgFhlzt1IcvT>>6k?6lRi1y$dbZ?UByg$p+6|Ua5tqNik;r z?<)}z4BCa83QB_ey%7ynu%H7o2hTpF!W$&Wr^z8;iIm+z=77uYSxAWv)NF&6-CR63 zSQs20f^roz2bawf7KVn0kiwe_RBUd5$S8r!%?A$=<)*+RaJflD=?z-00xIW`OK*@E zr1X{tmEI1Izy>o2f=cazM_|W*l*qW>hZc=aNWldu8*S>K=|Pg2Uu`wWn~?kf${?vy z%pCluk%9{>=*-N)a~dhQK!pUK4u^m$QdI{os-UH#Oyol*hWYO}7*aivYzO6Y#_*MO zY!E30X8t8ewu1`JR7GYE{_9A#g9W{qIe0RUA(AM_CO#un7lFjwlVowah(YG)BPIsB z$4m^VK1fai3TMZlrl5F`5HukfSM+$D$E@GrAW4e1$~)0c#+)+(&OF)${;d5 zPnZ~*o-i?_1~W6Vf*8WiPoa$tZDyoZdO6^vJE@)xqFRfYe?F4cpg2s`X6E33j1(GR z!4PH+o*PJU0TSf1z>;)Tx#R_qlP;)ZtELWac5q4FU}0DQN-f&V99$dMurNG$gp_mz z9y2hcYBQ^Yldi*Ka0&xAJPICDG3hRV7z|0e7ob)m?`VabH_O)mEl3QR`6GM~`506Z zr5Z8o^Spn6NE)D0iBE}xA33>z#MI7n8lt3NkRW<`0g1utcLq?(17`~zaB&7IR=B{$ z$P)&JR0n1bAyDyQ@PvVZsA2=uOotX5@cI-UEdm*n5Rw@A>FK zj%G?_LacUzOd&0V$?Rf83KMYJt)0cfz*~qkFaR1DOTEDbZo4`hWMD|WDGW|R4F?fP z=)gg65<+P;f;JO?+O8t}!7QFpmd-2Ir1J1wrFLf?Q1MT%bX^2}c+hz#Pzs z)B%v=VFTtLjvx)lC>(_~`yeAS0Y{DG;)aDZ8WrB3Mkp-b$ zZe$^Fms`Wagq6Vpsv9&gBgn_3AhLl8E(R)?B{uTy;$da5g(?G0150e=ZhXbc;BbtA z0eUK$8&npQGoeUi1>xX7y|>yW3c!|7K6qwILil|K+5tBCm0yi zU|Alf1JregXL%GsSe8c?glBn}AWD`;7KFM2oaJHSC|Mp^5SDnrS$+e^&GC$oEdKz+ zfn|AtlZY%2(+LI1bVL0qSm`83(aJ0aUue90CynZH2uX%gWGjnt>tJ zn~{ZqwP`041IQ|{x(VP`pnDY?WTey^b?69ObozkS&cKV#E^rI5uZoR<4>TKI!pP-l z%EWL0WO^yI=wvvQzHw)+tf&cux1eh*Y;3WhJq^$41$qNEZnO@Ss4~w0X1~F6`!#(TmUn<=DlQP zV7SV_09rl)UiJbt2r@MdTlRu12(4p4LW2LVvoIK3g~UG6Xm33@6>8S9F?>DA#E_cE z#8nMiMR1jYAvFnGLegMj5dawlG82SBb&;S36AM@7T^5E3AoD?-UQoGyl>vN?CYQ=v zCWa3n8674bu7ZV33<}p67_|4YigAVhWMBxm#=xK-$s{ENnyYWP#=u~_NR$g4hu4tf z5X6DS;dSIVyp9=%pw1wAdIX6<(<4e8f`%W^;}9f<5r<$w^f&|w!sC#ELBQ}WVyp?Y zh)jDgE2zRn3hxGR#to`vW0-M8(FfgcZ;NTDv_h4fXxXHi}bygR=RKVdT14HC>CJt}}6o5Fe zvlWl*@yz@XN~&LI#As!z9P7WR}J5b#O;(*qE7`|j>D7XV%`!Nq`kp;LD-OD7)pN%GnvB&~cGiUsk6mUQ~ zz7-^<13tc$L7)}OA`9^77}RSFposza=oqpfwBQlQM+ykg8U!_N4mmE1Q>+XNK;Gcx z5a9AY&&u%O4g*8LWF`(S?Nh7_4tE(C!gQE9xLi-MGEBJ3zz}eior6p15-Y<65GRyf zhBpl9a92?20lFTKD+V-IdXIr205rRqb%B+^0mQk)F3WYpi;1D(9s@%d3$rx1&^>UE zfq}123!LW}?n4S)lroZP^&EIHs{_aiXh~9VAE_i+a38ZI0W~Agi*k?{G(;H~z{^%I z+=uu8)M8+GfYf3zc!1PmNC0slEe2lDh#qnb@%73xa9sce@dI$kh;S{w%gS)!0Ruz8 z1ST1QS3!sz%+0{SpvK1`BJdb#iVxJ>Qsd{45}XA}p${1t)C4&|L&`8a7CNDNo+l!0d>(&P;&I`~>e`QIbi4-x~fl0kM9ND#7026XSFlp-sG1cws; zaTW$;AurGghAIr~EQ+iQM#ot|CwytK^DDA4SVA}q+8`MT4mOaO1PA!e1(`WaAax83 ztP85x8O&jx0ab8};alFYF+6?4#!#`6QP?Yvm7yw*m7$^&bP~(ybsP+=t8cI{Kn}Lp ze*?0Hz0-t|LBiOd72J93G-hOw4q$>TsElO;Er%A;VP^p8>J%6HF`0=WK#iH9Q;9|X z^<*Z71~q1eh!742A=Yjth7BMIRY`%Zrc4aoP!m8=B67#65AGl-On@kZ=m zWzc4U$b*{2BC{og{7m@$m!o%ZwuufXJKg2WM=3zmf&*ZU}4yx$;?pE#mLL${)L(0fhIEp zw=yG#P=YrzgMbz@1NUndp&ncq4p}K^gOR2;njmOr!>KK-s477_8}_0Jf_5G7 z>vEy01no)@@H>Jkh_tnU%jyd=!v~PtLEB_bTxMZ73Uv`^{Ra124*uO}V&I)8GTT^D z6oR%t@bjYyf*r&aimDRqAi<-kf=CVm<#C~NwTui3+RO}{<{Uzawag3w+RO}`^(07`571( zSSz+OF|b|&v5tvA_qKtp2l)b2Kr@DKHf3jE`pv{J*$Xjc2`bqc!zInw88|^QzUVUb zX6y_CAemr<48*kOX6y`#Aek_98DDdD1`Uu*Btiya&t`LW1~ZUM47!Yz1v^7DNG1tg zrpbbxAq6CpiZ1iPf}NoVB$I(I6Jg2DPz{pFMwdBe$Mh3P@c_s!9h%3B7Mlf(pzRn6U;^t2VhEqQo7#!Le8HI&@F)+CQVqkEH zU}R)qTf)cy(*;t_fzZXkb{14(Lres@hJyj_C4^hy!-6A?V;;NWg%UGcv$+F|aj&dYg>w>uxbKZ17-XU}RumTY@R{ z86pG(iv1Ne;{GlQZ%GlRogMn>ThS7wHluFTNT{)jLF zq?dyMZUh5cl_=a{igMq;Ul8CoM^noad+-nVZ=3EI*XB;33 zA{VU706HNXWFNx=aFBy2h@7l0Jax^v2$n00fTRQlHgSX;NRWep14Kdefp)_}3xjtT zSwMvWzdbV~AUf=r7^3Z%zyTqA(2j}W8;AqWqQ7+Et^_G(WZ(c%5EDy`7#I{FzPk!> zC5VE^WvMZNe1KlOTw-D1;Nb9m2#tYb@=Od|3QUj^#Z!Tafe*w1XVJHYaBqT?b1=Yl zF|f@shs#+%0tW$zf>?Zf2gnL=VRjW{8w1C(M^M}94zn=qI0%Vv;q8Z67~ULaf#!`{ zX7ES^Dd#}wVqkk^2akM#$6(t)6vVcs047lBi{{OY%OE#!bUcPytfkD#kfqGZ;9$ed zD7;mfl_3wrk!5BSo~Xvka8->J5&$adtPD3n9B=?Q+rdK@WDo}f+&l)hdtUGWD18EU z5{QC0Y4S6;SE3*#1*{YiHjH3mP>O)Yb5;Zs!+a126wl!8*up`-7{J@D9cow^h5c1o zA)GuGMh3Q4NC6M>=(a>qDi@w4&%_YT#mV5%%F4*VCaKBW)tPE7nLzLCcfGexQPzEZoA<8}|v4ZLkBuD*HVud&=l!cK&*g}Ps zAx(vq!J&zXk!^z(E6nN@DR8U1ub@N~16!mHT-ip1GN?yD5dpTEfz^Fnm1Dz+h9t#wffolnG+Y zcUDH>mMA8OF?(4Vg_UBMAjV8#WfXR*hFTrO$|#)Q!~}6vDJ!G!3TI}97tYKKHo z%#6Ykj;st(AkG_RM&W~wtPIRftPD2GL9K-)F5p51WFIudChM{x>|@enL)a&rb)11= zKFH(joQ%T1k25f6onTjQ@W(JQ*u3Rn6#iMo#GqddRi@I!#1P%Y#9#w5AkmqbVHzk%esM4g|LtO7 zu$>9k&IVkB|XJJqV zHMbZURG8S0F)}dtUk4=)1}*m6j0_A35Do(yNQRLSsr>N!X&|NO3I)FBf<%)F_ad8Y=sX=fXqI69aKI*ZM_EJKx~!Z-~f$7 zfYUP@$X1lW351;R#Uo4%d`FoWlzuZY3M-#vVu(4(#GrJHkx}^kT_y&Odr%J7QzVX$ z8!JPr8!K2D1KUK1>l|(%514>lmwW@sbtMoE+;vN!O-Kf0*MS5%7&t%_*yX|#Dwr6~ zR4_3p@qxmDfeTb>Dy>4Y2@?8eA%;Uj{~m+`u}Oi0BTf`j6GCiKfDF=r1UVQGa>C42 zObqr_Fq^cw7#KKMltgZ_Fesn!Vq;)oP+qlk=yN^)LoL@+sE37PV83{!( zHikGcHc(1oU|W&M!NA0*v>)VDXp)3D_3=$`l7u?-3xo%8Dib48X91Ktm>_8qtRG2E z_*@w)1A93vru76tHItIoE%cc7y9JJEs8#V09>gjE4y1-I$SMIy1c2%-4kS5Y#ac#& zm|8{#B?%@*VY?P4hT0Y;2BqnYj1q+W|oE(Hzq?j0DrJxR4e4T~i#dQ{lyH{JYGQ6^81^ZNZ zRv0V8l`vKYr3z3msvTk#%N_Leu5kz1DoY3tZj}-?t3ZM*EF2&T;(3pH7KWyJm{ltwR^?+@ zH5pxbLI%0K;(Rdq{o~fpFl4>j$EwB8cH2K@J8E5CsWu z#b73e*kDk@laWz)^-30o=PM!M%E0zb6`Y;I@1X}}6~rcppSmF&h_wP79CKD92PL$u z010w1AmoHkr77lg z16Ub$fIJumDsuF|eN!c#2k0I&0QUi5^`0Yy2X{onMM!4|#StJu4h{|w1xYMN;#nCO z5?H|*fPqb39~_qD7*@@K7!L97Y6u5nlK=zbY_7& z^h%pBthx#@91^h4ARLHI92^|)S&(xIw73Kbaxfs|ggXzhFdRMvs}K6X!vjh(kI_Bl z03IxW<%d8B4`P)82M6zE%5p4UU)%Li|`*`CI$;XNIGI*)6ijM zP~cFSf@CkGrMn$s6U2unARLIb3LG43Z5Wa2A!v#P334zXq1U3kjSWFU^r35z@RjfQ6hdZ1H=Bs5cbLy3=EtrA?(Oi3=BGLj9@h>TNxOR zYz2#hcJP9xNf;ofM*3#3F;v^LF(}PMlUSR<#<0>7MFO%Jj5m{wVS^)z1jMM^Og4s2 z3y_4w;>Ao1Y)hEHb{nZMGfY)s2C;>C%~%);%~%+ejal0x`6~a7&t%_*l=OFBqoNa zBqnHcW?4Gere7E~$-V#w3&bWp2nTLcX(W~~0ts?3aDXU?O()Ws7#K4kEe-}Y^-OS8 znDPQWz}g{(Lu{G>;XrH>;NW0NLbeH-!$E=^3yT!Z$atG6-#iby#0i zfIWB{!`gokn;_QmzXbahVl4*;hdMS7f&@7j5OTt-l}rqFl~8MMPhw&anGE4Duzi{g zwl?S`x^GJ%HbJaygK*&1a-;Yb8iOD~4h9Yo1qoO2DNGCjQ(!ikPGe#S;85BFw@E;S z1)Kx#Lkx%5^Z~+w*c8CQA?k-5t^tr72omIAK*$NlO=Du1J`G}%u-;h~hMcpo%=LOI z6DZ>wze0GB8EkFXD{xRktWAe-Al5o?aG0Zb5SsBpf*cG8IR>^pQ<*?@-vlJXA$8wo zhO0>)_ovB4hDoA16%5J(A0&}TMWYmUL(0&5yF8OF2TW(j1pkb zS`8$~!GMqxo;aO};nZ|UfHAN|Z)9RH;7|&GjR+%18Bql>9O8{`2nS-50SCtwl#&

efMD-gRRAsmQLI5;@0ld(h= zND$Pygvkk4DKRr_Q(}fzDmrdpoBZFQ2U-!taEMLK5Dwg?|8Jq50w+LdvH%HkFmQk< zaF_|Nbz^3D>Bh{U1e(@jTayj8X(xtFcOZsCY1Wl2{yU5J& z_#!i8+~4yuGehfTW(KAGOpFX{HOH7igWrN5SkMN)O+SF$08RF85FW%494s7Dbdena z9d`r?vLML`&pyV?aQzs>Dv7PfnHfGEX9jmggibIs_@7_~cSU?SSQuJ4U^(9DG&6$; zi_$_2_Z)?|0P3D=5FW%mA}kz7Rgv8z0tpn5APbV5aN=oZhPkI9?h!u9$-?lD6K3z3 z3t)RCKca_&?MLKr@PqK+_Ie6ou@@xB!omTfAPMZ_1!e{%q3qzLx%-+>+ znHgAElsG@Z{c8i-a|Eu~^*@2V3`u?t5Dvs<78Z`b0m#J!3nZjLf-DROIpG~|nHj#l zg|^(gpRzC;U;@lIER5P>Nhh34~xE#~`wU_@Bq+Z_ zI1rmOI52I(Yw17@`+kHdwpg)8IvB`mhBaRn&>Hyl#2MK~ETw!v;J^?HYtpN}}Nyr4U zFhmA|^9cjnvS6^S6ESSv0YAErM7JE$lZ#co0WuaBx_qASWVd+64)6 zAjt{aB(N}4B|v&@!p9O390s<(*({(+{u{_%XhQ?ibCmpnR3>OaI1p<&SUC1+A!iwA z9S;&@VL-?U*R!%Q>}F+yj@F*f1>2PH16I_*rZHO}hC_Wg6~cpCm2wfe6%DltB*?;Fhg$-YOP`wX0y6 zFnJQVlv#jb?_r2dknC^;!hu-J!NDO>kDLjir3^@rg8?BY+&q9yVk#uz3T(vLF^Ua;AlZ<3qW%RNRR_bPWa|376$Rv(1iJEEfS|V zmlYu+l*h{8od@gMa;*hNz)B1korSmo8UgnpJcx@VI5@)AA%_Gs0ziTsNOHoeYgrf) z*FqwIf$hg8uvH?z(LHbR8`<-o5FXqrt?9@q32GHckb{E*M1fP1u*PNl%hlKOu%gY~o@Bdk|t12M336DzZ(`BmolS zU_i(TN3pRn%w&U2U-C(VZE|2l54~iF;SifjARM?>3Ya334!SfG9}l`AV}fv`Mpp zC%72cmddh$+H9*pHbL8LkU_k2jI2nj67NHJP^(xtwtJv>3er^t39=x`32&5TWB4Eo z^%MhZBO3z)t11%%1DgvIXmJvIYZW`gOb_tdY8@s9hX0XhdyY5h%(TSJ~)cmjyc1XeL^VXive2-TKNP~cEb&>>^DN0JVcpEJS>Jcc*2#j zK$hx*V!;5S3^XDLF>QY&Tp3Rg+zXBnWr&5tSC4{*_`y*RvVwyF9H%EC%2vg}EKLIS zB{;y!BEXdoSebq-*bCd%Kx2V{RY;V9fh|%D9(u9CAgejfy@#q`OO}MoO@qiq!^TP3 zp3A`HRzT#geuC*css@+)2TleY3%?TGAqn(V55{FhXGNi9y1St|0Ud_mG2gC+R=pSZe2t5dqXgJKsAan?-qv9CWfaBOkksen3))MFfu{xu>vAxU;;`_jCVXJ-23>_dgNW=Zl%nVVFAQA>&m>G6}*dU3A6ImFHC$WI7 zySkW#L2U^O$W&pUB`gfHKx~kr^kXaxJC8vWRWPwKB>skm0TU}j)E{Wza9OZ2L|d?e zb@y1XGTZ{OLAsM2SsAuEvVyH(U&P7~xd_6Z@|cz3z)pxsPam@~EZzk*iCvhDL0^y! zWD)~|FzW#Z2JHjT_}+1Vf#DN~GmVi^;?`jX2CgHZQ=b?mM2<2rxEzJBuO4M!U_7&sq5bnJYIy;9cCfC_$33w-IowwNC`j{B!FEj9?HtV z=bFXLAOaPRVF0<8tLZBP!vszS2IoDDJVGBMm>Di`GB7wP$#VsNVP*hr?_j*c#KHwS z%)o$)fq~nMkp+C1K>~;aJd`%;MKH z<*O_V4p5hXcFj8}^9XcZLlv{x4!VPaYv*4E1_f>g2IswuJo2FHdIGo^7;N1^Q6eGn zo`J#RJt(Ldr3#?NfsW;63~!HMW#BWu&B_211|0}0v61TlH!DK}Hv@y3H=Oe++b57VM$H3IJ&=6x#g~mi#SfHg`OJ7(8IC|DA-RDsD2o|Q2%JF*c#ty41|CEP zdBDTK0M8%-yhs_u0mOl3kOE#r23Y`N!!pPP5C@t;PC;D)@;E$$pa{Y<2wO2I(=#xj zWDpcVSO!56gk=z9L3jp15rkzBWI?PMW0Xst?;tK;q2b8_`3j@PlD4X>w1B2;Tuo_JTXe3mFi`N-` zYz!yB$%;FWpOqnj52T2%l%JKs5~>b#%euryzD!J^SPUWVUVc`F31Bn08~Iro4nUZ_ zuUHvAfSKUT5Ct_E6e_UHfFcIT416Hla=8cR1)M~iH&?(m_l-xLLk4OY6Sbm zK>!}U1z;xFFAI?U@(XGmieH#Pi@F&Y7*PCzEC}}t0|O$2LD_{dd}{z3!&8WN9wA-Y z2Rb!RVk6%{P*V;XDi9%XQw}P^04lxUO*v#iL{knX3Zh|6IWf=)qdy@{If>1`7#JS^ z0(qQKN&xB+khzTE!Xc~-e5@j@3@~9(NepUVUS>fP;W?*3Z0|^FDSb;F4R<{mgW#Hox1%(MnkbwbOpDTWaa6u$QNC?#6y&%BA;4%%; z-W3o;ROb$YNY#0PAX0U{0K|b-=NAMK)j8;rMqXHTZXm?Kz`GvO-UZnR!l2RuUY&yk zKo~3tug*atAdFI-g9JbrEC{R4K>{EQ7KByjAOR2t34$X9)ZPUNfG}7PR-J~KsJWWf#728sW2!FfE>ZVZ~;^wZRG#B z1rgVvJE6qSvhwiX7ef`af5RjWE^Q!MAq^&pjeMZe22BW5+Mo%6N^>+JP^pe81im5~ zSqOB-F}Re638IujFhNk)0A9)?3nEG(6o-S#AvA|Wg^(Q%6+(76R0!GO5Fr$YgM^54 zIH*^P;c$>3!r=@If(>k}3=@PH7{o8JO7pWDAz}}78l3oLR(}3#Xo8Zhj2yhZNasR= zR7#eBZUPoDVq-V}GVD65s-U6~8^Z@71_sHCj6x_!NrOUE@)9HDG8Oa?MR5ZtLL|>I z3Zl3HEQsy~upr0{pfqRmpMfFgKRBhrn--8ltQA!FbV?%99!OYXqrz!g#K0h- z%D~7F%D~7Vv5`??Is+raaR?i3Bq%Hy!zDm{!#hYuf*QmU8~H9^3cWz8ejuu-Q2ih- zzp4Re#@nH+4BVjBtb#Bo1h_%1*Z?q-57c^vg*T*)3vRtOfMvn0*9{ORsP*~)%mlYy zVR}G`1J;Z~5rZ`2z^zx9I4GS$TY<WETACbb&^IT2+*m3;;n)_|B#ebfZAxSg8YZfK_LL~4p?j#tCaj!b2bJ65e5dS zV?qoJLjSg~GB}7ZFo?JEaQREKGBk)VFo4Q7ZV3@Kh65rX>$tc@*ccc@85qP*v&wU| zTCgz~fH==trG(bHvN0rxGB8Lj;t*|?Rz?o_Xpk#G z98m5N=uJcf1?Z4#jLZvalwo9EkRT`)1->FV1axXUNIL@q?_x8A`$1}@mdNoU-v9`5 zv(yq1-VCIO0Oc;Jr6NKSR%{F##26T)mhtj%TOrH=<#?%kd;*~GheQbj14vAA6DuEs z5Gbl1fOI_)0LPGmI3$L6vn>%ufC?h1M<9zB7`V8%vN9xyGcbUvC$3UyR)z)Q(5M47 zf***3qK+F>9SKN)nV{0#0m1~8;sp>UsPtX{W`Y~L7bGAJcR{(wtPBj2NP|EyCxdE4 zj8YmT2ntV#BECkXU_ok_Lu4UWIHA=zpaZF)9UnARAh)6kf!vHLgi_;x%mURoC}lWU z5dAs;kRZa@3=9nX;G_;U+l3L50Eh~WT z6@X<014#x3DF<0WP*zBgWMDvKfe9cPXcjmCk~zmH2+jf@Kpa>WP>^C^09Rsypfnr+ z;-S}Dpp=U{=_BVHPy>weq|XCN`Y``b2W5Mbvp|Cs1A`Pa3v2+fj|nm0%mVzNLIP$V zs8R=4|DeJGSrAk&1QgEWU1*{#^3_x}ZSP;|< zKz0jQ5abq!$ViA3Q&s}Rym=Ff$KJjUFwVs zuhhX+i-e8_BSVY^mEaEEYK2EG@Hh$0l!=$6>XcL!7GD^gnkqKX=A0f|W( zj0}e$Zj)fpWMnYV1iMorUz3qxK9qezlab*Elx?oX$dIlDRs;77=+a}x@N=N15@=Wt z5|j)KkQNIcl?VCwCMY3-3u?t2Bq2~~EwK?ix(#y(REX$tM7|YB=7CCkiH&@qA#<2{ zprnpw1yl&x3WyNOP$fhNY>Y&=4kN>E9dJNONa`{&cGq;P}G`o`VESOkjY_!-6Ez7$Nhp zV2LMEOyCJ&kc6c)6L=CBEU{w(WP%nX!8;K$K?|0UUdqH!xRi;(O@>jTWhrD%7Q|k- z0Wv2G(y;A56NBIbCU7W72x&5d=VU<=pp&^TX)-f_P0|i$22ap}B<6%OGrS9jO0YhH zOv-{J(jGx3Wx+arE@NTvU(N!y`<4J?G8CjJK#+}Lksuq`SOx|O0Ru(`2Lo`_NSGKi zGNc%T+3>W#3tUjlh-78p<3zf0dkLspv61huG9s-)g&c093-Np1M+kxH3^jKydH!~! zo0>tTl-fg1d!9fvLHAA$9$zE_K?*^a0m~yf1tg{hz0R6zi7G2YfHVVxnmZQ{H>gkD z04~|MK~0to5GJU}@&LjFHCY5ew>?3|8XX`^Q1w3n%mg=C9>^dySqx;cG+98+CX6Nv zNDxu?qx6VDSKXntPoP2|2Z6H>1GsU-038HPkOdXb;6cy{U?$jM2S9GQz{Eqi%MNNz zVmJ^ah;SeS1J?#sR)!C<(8;^rr>qPLav*EDK0IS(2#|vY!josL3=JU8Q6?pn1`ufA z8^aEeAd(#t&gP5^#pd7?Cb7tzk>L`QeZZ2D;e#buTyp`m0(t{Zc{hXD7&wDL9To1V z+pG*5z)s?yf1j0M9aJ5tkp)gslaH}7Y=a7dibQaVibWFyrKt4Vs0u+TN=F4%44$Hd zG*}rP$U##SsKQi`2Zai_+Drg5!PTZnDJ0%NDIHdAqKJX3O$IJ44OWH;^3Xt@^^}$2 z0N4yJ%craiALOBdtP2W!1*DXw2{j#*OyGfxEC>%|c*X^V0Au)G(A2?0EK>*4){G3^ z*5DAAXt8Ew*aBsLwPs{cwSkEH+cGlL+d|j}Y#A9oK-h3AKwW0W@Krvn419AmK^}($ z0ceUCt*s482NE0kklWhOdIyx*Alewf%MK(qqO`R^sz7aR!f6~-u)u=`W+F%s9yHu5 zG+7w}6hN`Zw*|?)W{|S%43ZG2^&+v6`+z1ZLjza?_j1r=HkiqE`Y|iR15oUM^4<+l zKL%U5UO*A#QgEhs05idvzCaO@Mo{t| z$T$qYg9KrAFfbrSML=x|#&9VE=oC53W>9*8PLZPuq15mYRhZKZKEZ4ZHQ;Q@w*hmS zVGAwVb`l+Sj0`*Mz{wMyBth#C7{hr$Ls0QLh$IP;m)OV+8kJfA4ohy(Xw(G=6Fdq9 zI;w9YHCmhu5>M>6&++M5Ij6SU@L0+MoEB z2-|}TTx-x6_-tK}$sj=n2GDI^(3vlY0Bn8^Wh@#rEr6Ekp+d-+9yA0Zv5^ls(?f(% zGCfF$xJ(Z!`!NzeNDwLE^MNKnVJ?S`79iKN5Fzjc-2re2gR|TRFcX~R6qF%Zjv8eU zA{*L)LmN~ip%g}-zJ|m`KIFm(nlM0xQGhbIOaK)|4PYj?FxmifAi=^2R32i47f28m zUMPhTWa1heG7rGEfkQ?>1>^>B$T+A#LI$NU0vU(b4h9B^kM@iViVol`0?L};*f9nN zh$*P02`ZexVhr%X917M=@F5!qD*e#8(%P# zGlrXh#%quVXQ0^yd2j|I1Wq8J*al&g1OgHOVUQpqfq+C{7@9yNQk@tXraFO30f}=? zj12#vYz=2dh6rbf_yt!+2GB(|pgE^7H%5jwH;DLj4@QRL9$+@S1X&DDXEheA415YE zAnPF(rb60xeMtRCFR&1QBa#qkh*C|QQ$?TzT`-Sbk%3Fcgq1--je$W;oRfzSv^=vtI@0ChU@=%Ual4qXGBkiS za&G~(M8Qn1?B}cu57Z!KuweXiRt5oeq)r3ObbUVB!({^qiI90*Hg&6$E(}qXqy8!fF`?O@nfX>t=$ZOgV&& zAvgrw$pcN)F=&A81x?f$K$xHcApyb!FE4{I!OP1aOmOxAGr`#>6>2;vCSd6wMGO+K z;OvtRRSODm=mZ_I5KKFY7(_b*A83V83sgU(=??DbqX~gV9MFV74G>fza5bx-3HCXt zeG~v@f~(mEO-K}j8z3D}>rgry6QP2jL=O*nWI=eygAzWUCnJNMCnWD&fa(kfM}$16 zBm>nz8^E@M>!Al=COBAsLKP#|Lnva9aFICT&B*Y@8*Bl5-Vu~f7{eEZurXYM#Pm;d zRt5nruz$}zWo2*xGr1((?63meUrBM%aRD4g;HZEZ3`(`I zB#I&ii3$b=aM8h_4Gw2e(P02)f{Ts>ZD=@yijD~&4$0w+tLQiYwgp^td;l}SMTde8 z)Iw0v5dh+#TL>y8;0YM!ACMp-0mGvc)G=WU2h9b7GaXDdq`8dJIe`j+G98*KIwu4ZDRQqIWGrKNJZO#qxdRQUZ26Eo&=4Ui)W1>&pgDU6SbH%s zf|UW>`)mLm9ljCV``iFwg4%}5F;`}R8{O$HAVUH~&uGA&3ydZq=7fio@NNu*`~ zQlkzO2p|j+Le~uz1M7xo4^TQ`4DSJzcF5B1Y7M}ZpLJL&kCxA@?x0@$`nc#La z$gd!b)NVcimIb$)KR}qEieEt=?hufA5C-`YJ&eF&5Ql&(evmp428A88;sX)^m!3^6(uyR)!-`VaVc5KHaYjXhPuioB{fX^_&y* z5$ib*=p(J?{GgAto>Req0e%^4fB|AXX9I{0ThF-x#DT8oJOy>p4F_9Sv%i!`5>miy^J&d|&`u z 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 z zVF%L&YFvYlnJZ>sWN3hz1!@Sv53hp>f?5fzu*2(+1);$NK6@@2Y63V)7&&;@L1)h) z3qsqG;J|BxDhB)K8aw2wf%Q;9(9kl=b#@L0et{B5(1S`S#&A%XC1d~<`URc_JPgbB zPaxUe1u6^9_D|V4z=2l)6$Fn8J!2PSf4G-{p$aMp3I&#D?1JFvO8`X#hy%JPWI`KK z^c?_kVA1yh#6d)#LOUx1%X4-faKXb_25~aDz4VfugF(muD)ItcU2t(g3)T1RY@lHj zL#QmMNMZTF&dm)PPC*s~4Xc1vnnP8B@(#-fb_K9XWI>Qh9#F*<0CLc0b|G-h)zA*D zxxmirg&G15_>b&741$MwIT_wturaXwVn@tagO0;w4EH_B$iVYv4kN<>kb!y}92h5I zvM_OQgB=I192giFK(#FkD+fQ=ama#T$ANZ0e*l@q%^}1A+WM@}0bcR~-tr7n4QeNl zxJebXJ4BPSy69W(YqAe2wMFMD(Drn0yOdVJ+bbTI746F{i zJ`Y(8wmuJ84Al2y2W{0x5rp&s!RyunKw$t~UDMD3S(ydiq`Cpbfv(SEU|<5Z%D~58 zf^q}cFi_fM3=f8-s5(f4A1uYdzyOkEspkNdG9dSYg=4^JFghNZ21QX${Re9hMLq2u zEC%aJfQ^8f3^_!5p!I{d#DSd!awuc?CYaCSFkKdhdTAI)1IF>-AVG-F7zDTUa5Av; zf(Fgm5i>cU;bO*c)&wpF-$|SdEOXh}Sr;H$QlQe4WiGodxSs=d)?ILb`zCNPOikbd zspnBrW@Ip6WCO8n!I>+8k&S`fkX3*hnQH>bU|8llz{m#QZ3}h`c{{+7GZ(08MbBKI z@(w+7fs!ctDlIXH|!1sG%a`UEb9?~n*X zlaWZ|Vn|2?N2TEUUdW})prTWdxev+_<3J=ANc49maxq+l=;67q$jH#Z1c`nQlSPaS z8<^M_?B;Rss4rqKp7S-E!Zb=j4TKrYlaDe24}!y&1|4U-(jMl`{Y3S!H2-Z1VQIs!w!LmiGljcptCzMPo@7U zjM2_P90Cv14q9=d4Lt-NCI)JGX+sWyM-(jY!C?@X!o@H%1spm&l^To;8=wUXN*fU5 zBdMAC;DK_OnIJKYHXuk4(gsA)uLo-QqUi^T;nNQ~*cfJ4HdOyLq?-tWK?ecXF-oy* zL^?q;5Oh4(btX|Z(8CTz4J=Nv;u;lUD+ zBy=W)i$Nq69DHnhHi0F;1S|<*R8~$}q@*B}>-Rt@$VrQ1z!Q)Hw2GerPC)&sTnyKt zA;4eGz@QA3oeIlDpeYtJs3541!2MPj(oaGbgr@{#K}bphU#k^>%>ZOUxB{;{<^Z*Pu$`zwY}=4t2f{Egu+BgVA=nXspp#x;p#c`7ylsdaLZFrft`I`r zItR*UxLVKj3n54=4jdeysAUYFlEKAr2a+k#`lmlKxELHV!Pym_-9QV78N*XExfmSX zz=Z{mMie7M11p$Kd{+##!2bX!i$PD^`~YGPy6k3xblAdZR}7qHz$2!J6E_`@LI|9- z(7Iw^G0L+Wa%UBkYC+izxhn>$7eHf(#AY}8byksDEuae(7{k|Qaxr{`WGc3o4aJj*=B$AOqfep+iz6=3v zCre;MRM`_i>_Jk7Ack{5hZBLK5`;m^l0d^bptFiVd=Rz*RpXTB5YU-LAmcz7G{XnV zA=nOmBDTr~omm7j3xq-AkFXr4Tih}Kj z4aPJ=1(610z`j}m6$draV7@{YgZm0u5Z0*{^qIoR;OPM_dl7v?P@9%9yd|59;S3}! z+1Kttbj?76?4bS{dY|w`HW!0I4mj*VZUJRp1}1P}AC<$!aNZVd3D1>CMurd2SRuN9 z2HJ*ez>X;F6F}@iQrIKcm!T7vp%vQBtR z$BHQ3Ky7WtaQR#=hO9Z@I)ttJAfj{w9V!Vb-Pr1|34&I(!|VlBN-R+v9N?AiD1xw+ z?l3{Hv%xFf*@Td;Qv;=TkO4eTBp4YEu(L5}Ph}EiyQ+Y23@8-93pme#HjqG#0}Fyz zTeANMMyLgqL)z1rco^6~m)b$qGJyB)fgB0C>Y(Zn``Yov4Baj$IvIPlZN%kP&QT-%^AIA8R zY91FuE+oPPy=QPTcvdljhG7{6C(nScXo*A`sfJ{|sd-!sk7k00YuSDtK}7N-Xe6_- z9YqMj^CQJsPaa#=1MN`$0LyxyCEL&x3aPkYE3i=n2PEskTU7DjcJAFgE(X;bVweT>@Qo2SQw^E&dYR9~@MQ|P^kw^m zR5-$>yilfEK~rA(?@5{QLYZm>&3Ng*7Z~sfuliZg1;)Ba#mIed9xyE6VyG+t=K&tj zVq*mkNYe#c&%q=&~3N)skoCAo1 zSPTbhA%P1fP!o?ayrY1N;Q}PW;KdnaYVb<|7lTb9I4aqmW1AY>_XSZS5IHpnDK}gT zxfpt(CZ7PcF~II&U>MHJ6QEvX_y^7=EJa)lK1GnQbvp!;V_*P{K7*TLIS`Od>k`1&050(zVd+$Iiy+AwYPy|WbkcU1R)LG2MaIqMi>=Z9Rho}<4 z%Ujk-p%*3D%H4+>c z(5ur-p<>{`fL>E&0Tn}@WA=uMfqILeh4Y|WmcpT8pdf+`>H9;)!0Mp8Qjo=9L;A>K z@Lee=f{=V$e|@m>5zBAq&An2w4y*gfgJkfYzzN zE|)8Xiorq%G%Z*G6+;f8Zm1a8=g@norbES$L#Q7r237|RA!IRF2qBBXLkL9>7DA8_ z8*pv|wcQ!RLA@Z9UJvLba&X~H)n1PysDl;70XZi}fD1a{FnD`C@Kyq({cBpn#ZX%U zE>hU!kS0EGwtt;fQP;(T+QhgkGK}`GvnuMk_yJh(1rGs8JvP6Di{Uvm1RfAok4>R$ zvE`J3Uu+4^CZMyY!BYt?rCba>rQkHc6A;PB;J}4cdBBu{%S-5OuP`xiX)s-i<9RJ3 zLjxD0^4I`k50a?_#Jn-+k}sIcN0)j*LIPC&BG#UOZt;SMGcbUw;Ta;V$VFjG4#Duw)*1zyL8^3z^)TTFS+6 z7m|r|s(28qDhv8^I%x$Slem)mY<9q`cyc>f7 z<6<}o4FjIPh%f^c#Go+4=tanrXS0QI{FF{ z#F(W3&k})G8?d2Iu7M30OpCY}!96*p3ND7M3P@u30`fi7(@#L{%(?8C8x;k4Nf}u{ zX^??N76f@wiv&>D7Ck$FvIs_YpuxbxC2*0DRLRAVxfGHVJ|bNZvmY7)DC_w^2lNP) z@p7Q-A_58GOA2kEldhrR3aY<^%6L`TE71jK@p3Srq?%L)1_rQQ$f?E;+m0b0Y&(X) zDfjX1{Ja34xV+eFKEMxepN-l;^m5@~9Ckd)RA-dpm ze4ySCM2LX_B&dFp1Eq5W5(DQms&-F1)rgaKI;maV8xhq1zkr0O0Jke3ZHdFRK<|<-u_o|F}PPj z!Y|DPQ4N7p5-8Up7eAn}44v($hZlgvFp?Zd5X&4XNSH+ZNOL!z!I>n#ii=@8#KAn< zL2U;fNShsHk_l8KqOV~9iD5VtoZi62BzRF4ETIgVH3%ra0y!4lS0FJwzCsE<(0~~$c5gKoq5vd!tjsW3XNW+x1hKs?s1{^wU zpO6|PI0pqpXQI|YBn}FQ&P1()27XYWt%i%?95e*}A%y^JiUXrrD%!ufmp_HqgpNo^(9~_w&JIV^rQqT?m+|JX;=io z=jy?-16U>KTs@?m2`SjBYPlE=)q>L=&tVNl1_Nvb8z{SzSOlOHA)rDKT|Y<+BZq=4 zg62^4_9I9?azkK%7H7fxQ;uj1iymRiLO~M-u=GWxJpiBy1DKJZ<{NA{93}=H0E12#z{HS;!(n2eVHVhg0Za_k zNP|rnz{J4npu^$FVzA+GWHI<~IEo-_IGj}kJ%nb6u!1HGV5-5PM)_C{atMJcPh24c znlOM_4XSo)@Mx z+1g$qibT)`UQmMqE~book9!_ zomb#9HuwDFWMKNw$zaaKG24`Z;Xwi`gSoT>+doiI23E$vFpGhKL2(n4GDnUp1A{;! zD}$Va4EIu`3qV0hPI{g&1BahG1A_xdfs+L13lS!Uf<#sZ=^Y#l47@8$85kBMvN9-c zX5wex3hE$24F@&J6;Cor^7slfF)$>tGALePlI1+D&A?!g#L6Hsi-mz_y$~Zq0!Zco zlMT-zAx4G?Nzen;rV24K8~|||L1$|795Q8K_yAF;$-YMj5rUvRrYO(I%k~IK5Offb zq7$Ps+f_6{p@m!=Y#^hdP6QtrB(#K^gY5*m%1CZO@Y$0nDnWORB46JEvIu&pB3KNDOd+rhvif85q{UVxZ8CfgvCn5(A8&U7t_|pv^_n z=Y<(qqd*IUpn@O+ehPser|ZKnhZfIHa&LD8gc-AO$H# z7JxX27`XsZI3!|Z3OG;n{pV!34vG;ijys^Fn##&xZXv;j93!hiF>;$xnFACf2B}nw zk%UxM2E{v!{5+AuObiD=oKK9hoHw)?7(S#T1-?QWD}&-^MhkFuNJv8td=Li__zxfo zRfj@$C+Q^A37mw}5xoso;dJb{DRjDaB_2NDR#*&SQ}g8~7R-5YWs zfk1@4!KByQUKy00%ZY2A*AlSkc$*340)^!im*U&$U{oM4ImCU z{UQa*eei^?A0rpTY*3)oaR`_(FdWEZWianSO&L=_DMJRDGCt%%0tII_RLEy#kb|a- zfPCbX(U8x|peV}(Df1qHI0j6RGEblY$r}y@tPF~#Ov>Ogub}|R8wWrfbZ`6zd*di0 z7lRNJ7lU~nhb*XxRtWJ1a@>N(D-=QA0L86AA;cS0h}(ohBySx6abVv1P>AF$g(4(x zB@`ifYXOJ@^%mE^22KXMMotEES4Qx8v7lZhxSqEFw;e&NGg*mVjR|V7fi*);e%Et` z935-E9mNQ31_lQ69XQTpaJ3kA^eHN+oR1NQWX5fl$xehdPGB7lOQxKPI6DLC=h&>B* zNe1Zlk%k0FC*mA9Ie~6X0AH8zpoo=$1$teAK{52uX$JlP=yeOAUEqx2pequ%OuunJ z?p#>O$Tj5~2ju#MLyQc9Ilnm=rh%@0aAIK;od27H;RuLh!onz|kPNYNJvghbXXaw~ zxQ2~^g@=WWbq@nWB~%vFX=YJjQDFPM6ID<|jfsOb6irY;ii5RPlnF(*!YU!QEHp8m z1^I~#hi&d85kN$SQ!)yIT(0azcVsy0LiGa2=G*V zXJmK);#jZ!l0^ip)B`FASIWR9w1Zaf>rMs+ zhf=7w?}#um6o6!OStNPSi!d@QC}m}UTmU9w&%((dfOInohbZXELhwB$41(S)oRDY@ zXJKSuU^>bIy^KY7HVf!7mNTGSD0_yBfq_v__!de;g5FP%IOm@taV9@Q z;vp-2IVaAy>^4V|ive_Ox&1MejV++n==R4+8Nf!FM+UE}I6lzJ z+TisGWMv6xH3`NrJ!tg@C1-7d^bhbby|B)FNF`x!$;b$g-aFu;pQQ&Hz5JNb%P&HL z$5Emev`oOBUj)V9psqE2mkGeVstaBxkqGlD=86b=TSbbzYO6TFUIpC}eS-s9);dOW zfXdoQpyk-Ib9osU7#T!vMsqMoaEMNc=3vNdV`5-o5S<5OvWTvT=3toK1}bPocfptf zqB=|r4DQN|42%+xf&pG`K&A*qze5t0frT8nxBw+55Dw4$&dC5OZ44?9i7go%1L{_g z#AYx9Be5CGKwTsZDqo0PBn(Py1~X6>2@hamy8#Y_dYD&NVS05HWnNu1&|YO=V49@K zz#!rg52~@WTdUX^m>WSgmNuxy;sG%^AW24JK`^8#1X?r081C_wlR?9XjX^CJEiLuE zg`_35POh};91Ih^SQ*q7Gjc7u&cW~y#6HX@_!7oeU}O|@zrn%a8qUF>HlGoEpBFd* zf_iq0;dy3|G^RG65v42#O)#s?XEZ~}=O97csSIT$AZVl&q#q@t(|_7oKyx}YsG30S ztLy988JHI7F))ZMiRWPO;1FG_$G{+24~h)Y4KOB$Xj?4@Q5(Sr@_41UZ^3>*@o zw>~pN)LMw{(PMxpZ~$lfOi;FGP-TflBEBov)h5oHu4{oJmDq#tK-l#B~XDbC`k85bmmJN2Mc zh@jNtERLFS>7RJu?H!0$LAy6FJvr2OYrt!L$TCr@dT4xFW6b9}TcfVH0i{<`=JP@N z2Yx;u?q7Lu9XlQ7Ur&^%1=Xj{o~Y>;Bt~TV1=X?6o~Y@U{&g&98W>#EgU$?L4F3T0 zY6Bz63Qv%%a|0u4gZY z3{33l$j;~$ONTn+H^|UFgqZ~(36L{5`k5HmrXnPcr9+(|3X<4~yn!@)%Ca~Zgd7$?IG|;KjN#v4PB38P zeJ#kuZ~^25L$DJzXCXVmmho^F2ZJ%l3Ekd>~CoXpa1A(oV8 zLmg8Kl1T$OM&La}>RdJlgD}r|4oHG%VPq7a0CgE8tw?e+G4P(`V`306Vq;)y1v@Fg zj1wtTCNfIqK%LYDGJX~3mV8`(8Wk7Pw8OGpTsAD#Q3_gow<$RC~$T9msGFOmfp65axa~dS` z9oaF4c^Hlz}m>VD~ zWszhA^P!G;3X(BkLULzvK89m9!yMzt#LF+l#Bc%R7$>k}mggg<96zSb`B2Av0vYU& zWF=PtH0AsU$pj(E#1=pu!!v=2fh`i*F`EiF7=*>NI3Xz~mI-mqAS7D_^D;5;UInEb zV>Sl1IIxp`7H}|_uoiMKNP>*3W@2qWFMV z@7c}8FbQORAr|Y4@L4ZlLXPze3|wcLS|%_YqXo+#Y} znSx^A2A{zMH3BrrYs)3YI!%I+;TTj56jZkC3T)vDsAAxg(atKO39jOiXJFun+QGo! zu!)tyRzd=N%WlCYRt8aXMh-U6ZfApjhgAD;)&x;}mUenJuNsE!e1gaKPIf4ve zZ$%T7I?c=hwQvE*LMe5yg%^-5EQT6_Vj+qk!oqwcwQviO1tAtPfCHCdGb@9wE+lXb zKx}B>CTvCy+y_u2kpp)-R1D-KSl}Xyfx`t9xF~{%z?}fH4H~!yHX{YD!UQaViz0~d zs|r*tO5h?3Li`Gj%?}_8p@FNg1Ho|gufEwj5JZ9Z2PjHa_Iix|ql z0f|j#R~9jpaRd4{ok2}dP9X*cCSDLBx|e}LqahHIL_j2C`1!k>3|#j(86;jXF!AqY zVBnh5%E558m4iWI79-cUcN`4*?;-4pPaF&zKS9{;k(>+-k(?kk3`{-QjF4Wp;Dv3R z3{l%T86*T583il1b22dRfO3RSAj#aig5ZGM_fD4?VgT0{U1kRH_goBOag1EQudy(A zg4l-`1ycn$8MfGPGKlSFWE8xS&B>5{lZ!!2h>1~9DF;dBTmce?p$v&*RE@;RXh(1u z7?{eIvqG$gCkxPh1B~IVZy;j@ss`Lq9f(ziZVU_zs)ig4(hATqt_JYH-{Uu&;Hw5z z!@;V}ppszKk>Ke@rnN=T;W5E&MH~#@iZ~cJelsF!RLK4!(1J>K^yL*C9*n{af@ysm z3@iFL7&z3J7=`2Caxg4-%fZ0m0@@A8v`1jWWHml$i6A@rng>pIklEt> z5VJXrm>7lMe1MqE=?pg8t$>k1u%noRp{$URfoBUdBNML`3xnW>Vh)DuAdze)M#1VS z91Iid7#VoNnHU)aZ^ESJGczKk58geD3=1PU71Hq}E&B0(-z{tRp!@>w3eTJmtq}QAf6uoB#5qxo#9`<_;{3XUFo1zUFn9_Fg8?Y0zA-ZjE`f#HLuSZ|55YGuKdxp%jB&<- z)7UOkNI}J8kJ&lpv1e37+1d!|&*JI$V)UHpZUBSej@!^svS(yuU}6I$s(?8h3=MNQ z7q5?(?;#+VL4$DGyeFvghF?NEmyNF8GW=^ywpCay1wAm@mxp8$pVdQ%n#CZ>}d z3|xXIIT*Z8axiG*G9m^Z_`$X5+;^M|AT=8C+-XSrOF_v|BSQv73^Y5Sk)Z>L8m2|+ z3=CY)PI7?z02-A@27{KxFos`v$H@Rvqfx;+c^51m85lt8<~1rgxaT0Pe+I=9{}pc5 z`AF)(VmB38?LnnGR6AHKL5bZ6O^n;0Tb6-=^^^=K0YKD(8cF=O1lV38odpaM)6`+& zV84SV$UXnJb%Id_X;l>HG+@SXm-n0u2RAb@ zXjHRaKyou^(TGM32lo*qG0-t3{LlCp7zECp=3r=g!o;A_!pH=oxx&vt1Fsb+@E|L< z_P*z2$o6Gm(5T~hEycvp0CH442kUAigFy#pXtZ*$BL^hNhy2ov+-y6L6oNxdhEbgR zBxo@|BzVB7fZv0Mfq|n~jgesk$oy6g-ak@I3=iO*W`GYwLmUNi3C)}!5X#D+0d;~y zC`P!0c4IS!8-9S)@f!W$9#R2F&qO00P!DNAD5Mcjh3x>`=}7Gd&|s#jfAkK0oMnQ9CPKH=! zs7wPhC&Nk*XE_ri(*p|z2Gdu9oD7VNqR%WC7}%Pb7#JBuPd?*h@R9{FmHg_N81|=e zFbLKQaxw@vF)~;cfouG?77Pp`c@Ui-F%hAEQPV1mA3m z=$=CY&;27L8nuF%z?)*AX%ehVD};%ILFfXCQx?P2CqSHnEUT3WbqcZ|+$qR{Se?S~ z3F?$|EKbP)4UmXbfXYcma6F&~tcZ0cIJ!WAUNaFKM<5CmM+_pr^Eerpn85lJ>X{gr z7?eO1MDAP>T<&QLSPn!%D1=K0paXLFD#Sax!pm zfc2ei22Cw0fhdTaQ7v2!JO#+01fn2vOBfko`i@Kj>jO~`xo5&~ImpT}B@hLcV_=eI zVPG&@5DqDHK}9lSxcYBSh6PpZ491$=qEi_eHbW&rD^86~_!!JSKm|ZMvl+ufesePP z|K?;cwgRhGi+~sfS`uvRz`WpawgGu?IJs2O~oXR8pORfx$S0 zmw|!Fkc*jtsgRL@LGZ^74u&5ZObjLuIT%ISA@TPqhK*sd94L+iIW(CV9%wT#m`nsU zWWZ@3mINVIUH%P86(;FiY^4eiSAkT3ic*sdE=l$#doJ)wFO&Nytr$?IHMx&^b^%BX zPn&Y2>j#%N;E;uEvCV;n>{}MLAV*NNf+ZP1ER%OEitNVdf+4K33_PISxCO6R8BE@> z2%zl71&tuVj^%`y!~iyndAbw_XgBVGSFG?0)<1yQgL*eEc<1dj5mp-S#s&4gaqTrl zKHnKsFAw})Q z%P`7Ta4rKKerxa=OD?8&T2GCkvX6WI!FfmY( z3_JW5CI(gqJ^U6X2965o;kPJa@WXFmqTqCZmV3Zr)J+?pE*h@1ft-6lYl;U!?g1Ts zixNU;d)a9aLZH=_xIzdy_Y95@VnEbUp#CyrxRwL7_J7O5HXkYXfO>x5I;sI(5S)8J zr++@cmV5BkQJ~X54c;JfPXdTN=;|os(?7wNI!~kdMbF^#5FACgkuQ1%H4;E~A|mH9 zP!}CnE~DQy#;nMve}Z!q+UcKQG0Mv!qNEr$g$>3aOf-VTo zWj`Vr8792LQkUV&W#V;=3>QE-3tE>kydOL*MsQ&XuFF`W5Q}iYt;1;|ta1@(Vw9KE zVn|1xfyWLVMYsi!tOgC)lhR_WL|S1AiaZ*goWR< z5K=B<1?Ms@Cq!M=fS#d1{jx-KL2xe9h+<@Lcu!(3Q?6rVXaMCbXfE3TVh_5y%mgV7 zfzJH^U9g3m%fMo^Peb6G;-t<^Y>Sb8X^7PUDTKf|iuSn-bhslkS~&y@p+TL?Aaz+5 zQZ8GIo}oawtPfofoXdQo7#SXrn#=U-7#S2kAXatVeRIkgf zA}!E?)n!kRDg;vNGJ4%}%D}*yf)qlqTxN#kbg&rZCuDI$5Gx~t$PyO@ z(0U?}JaqBHWL-9dyx^8RPKH-`P{X+M!K+e0if_w-R;37v<|7#<*j~iRu(t@R_#R{l z3rO*Ih~gJT$cmjyI2kHSpo(Wf*0g{W3(JGntq3kDK~}u8oRi^wIaIL#WRVL%|eqF=KpjZo4>;zc`15%t0QS4KTtay1H zC&La`R!GohDS_9%fD|`F6rXlwMGCqT4V(=6)zC#IKOhTbK#J!>6gyQz7ny+Kfq{W3 zcOwgf$bHb^nH-|WP2t=B&zLeWfY$J^a0ni>5+{c4lH=<8cOc@FATS&^i#C zhfEx7X`od)5HSW&g=EvqsK6eHCJ0_liK%oVqXu@R4D2o1h@ud5jxNY924PbxPVnvk zn+HfMqe1Nv#_+SETny(#*coh|Gx302yI~zGgUt&j0TfrmOu}>u4PA{`^9Tt`byp?^ zHc6zg1g-J3`N+h<){7QwHX9ff*c;FUxzo618Bmmh%5a-4j2b9PL4shVSit^AE3O) zz>HYnp9o%$zEF${BE`wf1M*P=$T3{Z94I~mt(_&uXB$9<(b8v#spWXEZ)C)|7$%By zG1v$|eDeV006}H}9#A|fY+z-;@C9h;1xEaU1d-wgq>rY)fERw@U|%$PFfp(jBc&|R zYC;<+W)93!N(vOcJ8U4ki)F0L2djTrwz@^8$9k&>QQgY)n15#wJ-dX}6xQsNhqo-0O-e<6F)ua6l2Lh`TKq>+-I zA^CU8i19BZ|9&1Z{)ObG5&?* zU)`xAB|Ss(Z_kMFFC_oI9x?uf3P`u7m|NlMvQ+U<@4ha<6nL7J?Ty}q3Le4XK!YlW5Mv{7mY~KPA z>sqrJ7#P3?41D(xzS0iTeb_OQybLL^CFYH|SS}w)UWUZ-^^xRdNYQORe*`6LNGwkq zNnVD;^0$%XWk?$=Y{3Xh*pOJ>Is&}Rz?7)ZhPEgka#^kDLP&&ra$#)j@Z_RsV}~c# zKyT~-?_V(o?ZCB|UB|@0z{OyxS;N7=&SC|+A)bRBNs5U9iNk`-VMpfhA#0XUqKfdOm1`LoK*9_hwxDYXq8%6*ER!4<7}y!Xd~;3)2Fv>6>(OgM%6n-kP#Y?VA+MJ zvTshqOa{yD2g`OeD}&RGjiw7suN^2ZE;leS*xYMif|_Ufk{fisG+5&gu*M%SjdC6^ zjo{ci#>&88E857!aE_IM!5nnQ0>snI47MH~j0`Lh91LJn>>5$L?b!&k$~LZ^iDA-l zc8Ic6uriGns4GM3VamXUG=pWq9sq4B1nC4ho`J)5dLt8qZ3IfNpMaRS9jwO#X5t}) z#UP*5MsP6LUTI`vSRcW`VBh_dk-_0fBNKze+eRh^23wGKzVI_J*a~(qGF%j7fSAbH zgyNMTc&LHhsE#VDiz@4iD$9f_n~W+Oeg-858&PFJj)V9fZ0~$j*^BUW3YOiAD%%d1 z1*cO-Z~?-=2=U)bRJC84U~ynO?-wJ3kQoDmE$D`qZ-pETmVXO57}yzXU;JWZU@YQb zu$6CSVo)jKV6fl#i;=M|FwGKqGmA^r$ukNp@JEMYFo1}lT-^D=}o3qwYRY?!i1 zsLJLdlm!?tGW5fg?L<{}1fguQ0VBhDn6d|`%3dRssT(jdoQEl6Z$t5^P#fH*4F-%1 zuVBjbP?cFClr@?#G6+XQ;~*4OSpq^C=;|CHP}TU_je&uc1)MgUQC0ULR8MbXVlas2 zV6grEkCDL;X8LAS_4^R&-IW;`5Xk=(-VmJX(&;Os1 z;UrAGOgl=TsJFudB|@2z;R#HcGpaH_gtF9i3=GmS&_KyTRaS~n*0_#=p#-LEBC4`E z2xY6bFfcI2LQUI&s_ZaA*%4Jn1}&Jf`>4uZA(Ta_GBQNNl(BW7_*Ae1?o-h%3=CZ` zWxA-!ED*~6sxUHKg((X`RThs>=DLM}fh`W|g=$n~Z3tygH#0DV!;~#TRkjwPY_19; z!y=fnlc>tBAe89|GBR9-Df@t`>^DN0f*~WrE0{9zPLu#q>VyY~b|(`9b36xw?b|7g z3@q^+3?ds5EvBGO(2+NyNG&E?P-VDOlYv3xA3|9rrZS}|j10Fy%ARY%+g}SXl_^hU zWH_b8z+k(nlZnAoi-AEoTnn_>+xAc=6GMg;1GKhZ*$J!d!8zsnH@rzxjG|*FHBh!sPQx1)sInWV${r(>ePCi@m;|%*KdLgWZn&2_nVA@_ z!j!3XqlAnhLfJcJCI+4aXaM@6DvLlU^J8XW(1Ix|K~+|dP*$bP$lwH1HXBvhGK8{B zWk!Z-n6g8t%FZH`DKs!KY=J3ziK^@iLfPMLCWgHU91J4+62P4yP}5(!2O}Ya+NBk+ z7Oi;?N`N@^z{3sHygLT7G!9i+CPJC~eg=knFlDW%$|fL`1@32H&`*R0?;2EP+Y!p{ z>}OyIfhoI;s_Y&@*^2!P3>7eCzre~YV6`@DFWd`L)EOCO!jviYqWDw?p$yce+6+_X zfvPMRp-gQX1H)06vV2r!9H?#KUQ}5tczYI{i&vw{ZbMk%w2gs*EeRS*mr#}6MJStg zhJhg(rtBxGGL}BL_lveMFyz9NDfFRuUmKxp(KZH#doX40sLFy6%1&%yU}#H*x-Ab? zStUZ*`gIHpRVh$qQ&E*IKq$Mrj)CEA3I~HoUn&O!3lq5G4r}%BI{8?R<;FMSv9gUVPs|Y$jTUyl`$eKV@8&d zN0#YEHlPeynGmuv4`gLE$jTIvm3bn|2qMckBOAbptc(>|85@I$ULm|}FrR=?HaJXR zVh}ClU=Wc3DPsXg3#hzcfkaE(1SSR+76x;W5G1^rA)T_i2`H(na{@dOg8EKh3!&*@ zDXOv!2xYv@ObiS~P-SOOm0d?D6KiH-P=+b{jH>J(LYY=G6GJ#mnbbrSx2aBqyX~C< zBSS7snIoz)AA~Yqe+Gtrn6eC1WyJ_(pc^Vz6mc+ETDdYXutJ7YrlG1{h)}(1A``!4_0sD%Eg67*;TbGl&7oM;q31 zFo>L62ak3=@MV-JZJT`t3=HemaWL3|S}!d>I2dd}?UP@BI2dd}QqS2r8Ege)nHWw9 zLK!+jP)3vxCj+>X?hNUq+cr&NVt8)L$sqF1mXm>r5uAcRu3&_uATX1W0jw;?j+4Q1 zeGexCyMSf2FDC=50N5m0!m^yl2hE@^dW0 zo8ZQ0_^H=i3?R((mooqy)(~fdt_$a?!{Xg+0n8xCK@9>>2yx}2ih-SyhZ-!Pb6L3x zSQr>Ul3;5gA%FEX7Xt`GL!Kdtje)BPi&M}-9&8*9LLN&fGjM>8H3K^tdhH!o69)t9 z-HlufVw+GCQ#7cFAAa{W7Xt`0{bk?A#K2$x^3pU84qZ@CL5%_3^8`vvPmvN6=+rP! zV#+{@XwZp#pu|*)E@sQlaRFV-jh*8zx|lmR$15Z;SYrB)BnGx41XUd~88I+0aLq>bF39Ix*#ekBkR!l>90VX^ zK#2)i44lXE1UQg`1?)VO!~_ZX53jixKo}bG380XlkLna`ArCqpjTRw~C6uuwruiHU zkl7=yvPE1BE8cN2FsCzei7(}1xZBUcz+BJBb#y5ggTgW{2If*muDF#5HA!m_YPi-S z)Xdw$#ZcnI&cIy3$i=V~&IVm_2#zFBbTEcHzvE(92(m2;bRQCugd!`XWCII<&h2Cj z4`gLv-~fq(#XvyE(%g-2P#6rf(($$s2XHAyphB}r5AIH2m{;Z{GtziIpi5b~Fs^F|>2~wAeWH%`KnQ^#>fl2&5 z7X#~=MO+N5i@6xsSrNsj9e7johSyvSAk6fay`PPNK_P*Sf&B<-Sp{+jyNn1%vB@qg zf>LvUjyh+TLoGHzWg5G@04I+r%vTKH(`MO~*f|W5`~b=&oTUN`98yTR7!*_>g~(MA zD4^MW*g0k(l`^0bfIUQx4vtZi*Pt1g#b9{OV~O5ki@{|m2#u#1qXAv5{h21 zdF4tdAq6_&jJ-mJV=1~@Dr7h|Wgwym9LoJ1DCU7Vtc3=|& z$J!hjlq?7~Zw^03Lxa768zl_DZfWD@lt4;Wpd8QMA%GFHoSgy;9H)^y40cQ>4`u)^ zkwft?H~^Q(p?D7Lz@-u>0SFTVB|lgYf+K02C`w>~BWax|ioM_n*~rIn1Ic}0y&G9D z#Wt~^L=rdE_p+e)7pyKr45Q`8uELGtU$DKZ3Ml4*{h=z1 zsaFGwUQH}|HHA?k9c+gVH)clCLromuutyUE>ow;_F%KN)HmHdg>|c8oOn=ymGcd4j zS;@ulb|q?&IS*W9?tIO~0K!av*$;6uFgSn;hf}CEGT2!%A}Eamu$Zg}O5*_RR5{cJ z16WL6fKvr2ZG(eXiJcF6*Sc2GrB0P=i-8V9lU5m?g#kfvzDnm`T*7pGiEMH5I6RGf+Oyi#ryy`U}{d$|&dUYHniD1yzakm2};6tZCR zDr7kJBe@SO*3W@r9$4>WZj_J(i%ro+i50NksaWj5CN@Wg1GxnOcJmy54)mrrs5r$4 zgEnqX8>E;AwJJcxDSFI;i&GVJ&vo)(2H+Ao6c2+PvjjECfs@x#36ubYi6I9xID*%S zq68K=VAhGE*b7#-k&nX=-F+KbFvT{pphObb-t8QicI*;DQ3tkn7Y9l}gClM)3yOci z>N3PIv!V(&ihsfCR25Ln1BaEWFs5D&EP6Gu=+zX)jC37t4vdVXhnhIRnFdV^>^^gD zO!I6|6E9euy$Xsyz~+I9QwFBrTeuMYR~E(Q>0`pXew%D|8Xl@tN_uY`l6K9hl=4k`v3{@^a*;ONO@VE6|W19iq>V&|Y@ zpi7szOE|#c6}AXs8K^q~QU?mJN~jnpV}U)v)&LcOtnw{?!^Hr?P$zXmCBaUrgg9wF zR1EB-N{Ex>7elN9I|(ZG9;y!Pq)JG*)h*u}sh0ydR_fk6rs!gZ)EC`5?BRDo^-Vho@7hKm7&(X0n2 z;AV)gU>ZREQ0``kuV7+uU!f(6`ER%wo?uHYpWbjWn7;)jBeo6Dkog4;2iLb;3?R(( z7o0M7KqbM^Fa;6~7E6$#0V>7;6$3}Z6iCYGgNlK!;ozPE35TUnF+@0QfQtMBTOa(E zivfgT)_zmWu&|p(em2!S;RN;MknWz+kWp5(J>W2KNUfH>841sDI1F0K!avF`AOxU$OY& zE0PJdU=upuaxs7~HWPkf@x?C=2DSjGqn3hAnEaND0feC@z$8Hd&;1|biw95*U|;-4 zaswnT=Dy`(0AZ%T{OF_4+$@Y7{0ESN5*#=zj36_XLxLt5?21)yxfnngW(HIetelgP zga2$M149^85F8zxATtV}B5hzZw!h_K0AZ*ZFiEf(e2g6Yw=x+R9zk`0y}}1F1C}!( z?l|(6ivfh0{_-Oaeu6_o7@J>&!Dg(0_@xZ&j*D-(7(f_i22>Jkh6L0v8Bjs6UnD?g zG(bgWg3Y-1mWu&|p=Q7&!Dh%p{lc;mVk_7$vW%cHxh*!F46N_Ab1`V{;9}rmVPrDi z$;H5Gxs!{bVJ8;@k31tH7Z`&(JFnkzF@P}BUxB$23=9*J*cf>BGGY!8@hD@CtMjM` zV04~&)KELcpjHD&47u|R@&u1M7Io^PC>=tOzj!nxP&&Y%ni^dURP^&`$)KnMmB&2V zG8h9>Jo-cqOo0?4_u9c07qDQA3i1@HqLemZ4;QMUi~@kldY(#Elo2sd)bUhFay&=M z9AG&=BBvmu90SVe6IkUsG0bS)CXHeXI0_G-rekp0J|v5x4(!4sVkiSXV7*5; zICGI)2v&DYlmofE1c`Cx3NWCI(0~MaPM{90fxNL@Navlxk>r~?&3 zJZ6|9IXvc=BRM?gsG~rj1i@p4MXxm$z1EncKs7%}eG@<#VE{Y(2WsaU940?R zG1dLVqVA_CO2-nMxqeAtbfkIE#K3m^kwH-h7W*qBgg*Gi!^ntz^ot*T^b4etAG!Yz z_MQ_X3qSgN4UaRUDnD{V5EQ9kLFAzaQ0((~Gph0U!GWL1$j4uaUgD)P3Sv4mlaT{cFq=^d+z^BXKiJGnMh+oY z^ccxt6y%gfuQ5My@bjUG;R>gGY;G!K6yrx8p#=v?1*0H8^0*|Z%gfWtXeHE&3bV38OSW@<=3D04xvd%t_Q2!!YIlCYJ|hW9wZ2=m{6Pq*1nBVh97x= z1RPA;82R~;r^CT2*D;EqxDy;qEQ}(UDmfUrF)iX`RKT=|lTjE`I}bMPyx6q!G79sf z52x}7Fmelm1V4a^VFnb3fMZ6CQ3cZ>VvORLCdorhQb+;Sb|?`K_Pik@w*Y9|Jpd$c z#HdAZ;2ji>po$8`QQ%rYn^6J9e2_{Wb4G30?lc z@~WY>(m`V2xe(;)7$gW%I1eeQf(mY4Eji9rNMfMsm{&)X!xSm+fy6i~1sG69enG*; ztIL5gN6o8;I^qmE$b;7cb@mi&u_YhMj3p>Wd2K~d8XF)n@DMw)t3f%0*ACUypamYh z4ydjMiGf{>KF-VQD2n1`a9BHO$KE64~Y&`p}Gx%;>nmhIzW;A26oO!xsF?c{#P#Q?%gf7vzF85kN;*ckY>qb3=U zVSMtK9Zx<5%#J6Y5^6&n5HYj-bR-z7pfz7O!M(GNHqooZsxWV>J5JBlBg2ceVeHh6z zp!~!)odcya0rt!qc9ggWTf9~R#eonp4wUf#uvmo(ie7Mf(8KI+@fl(EnfQz``%HYs znEfq2EPXXTEPXXTbIkr0pA|R8ssKJ~ew083yV(}AZ(Wo6Pc?Jdsz8KUF7O2fmbdQZM5!FC3kby~94FvVrBo1&f1Rg*wuw=k} zGLd&&3?R((m;IJD1498Q)$YaYl7R{=^r1sgfrZ@Z00*2BW>*VTU}30J#-dIcvr7gl zoX{J6=whJS2UK7os{DJJ-XjAkMDCD*V>X)wW#Sg>;Sy6$ z?nG{-m8~JnUV#k(KZ>53rJ-FSgetQ!yBn! z1DiLG1H~txVBniCfic|-D%e1wumF@S7PIhy8oL)j>=o=NjSR4RR&t;OH`tyDA}Gs0 zKw{wFzKi5Y&}F`$0t-3*KnnTRN}yN*5koDUz!q1ipmfK<=|N8fV;&k*IHA|{pu!2c zSr680j5&u5Dx5I%nq$skg9;~5qZ-;|1TPx|6;2otYR!)ld0>CoV$MT@3MUNnTu|rJ z!Ft`X=yeyznE⪻l@}<0xF!4o6n&6U{K+NzK8;6;e_1N14mLKHz%@zp!PUl64quv zD4du&4stOt^&I44V4ZM~i{b1+E(U%!M%FurxELf4gV_vh3eZ`B6mSDw_8k`k2s8a< zZ!%zDU`S(Q;8)_{d}YeO5CK&PYE1C+32-=Np^9-fiZgJW$zWhegen9jeSRSx1_rhX zP!Y&1mijv`1`uZY%id?ez+eEfLk(ib38+G3JCMb|cHGQhV7LfX2)BcQ$@4wZiULrP z&lqm_j*9_=nf{`z>H?K60vsZUxebVFt9M)sAk6d^rHKkw%?p~sX1Zg+fS9&V1vg9g zz2ai{@`{Urkry-!3<`9HtKj1Q_bV<2qt{#vjN)(!JFpI`*IWz*APH?Q%SBua`HQ$1 z7)=qaUCy11Fwcqoe>t*|bS?x*&gWoY{rjAY!S4mCofANk;hD_b3?R((mt!fC&VG>O zVh#q@k{4VIN6~ab?3~QZ%>cqoe>v_W=>*%koP&Y&^$RWr>z7;%j9CbO)`5%--^Vk~j!ok3Lcqoe>sjL>5K*G z+yk0v%z6mDRhetmVlIYz4U7z&C5&9UE4Ua|PUK+V>|o^TUWE{6Si{Be<2Ew`=T}Cq z!|M=gK5XVh`NI9nLG;%_0?9=8$fy>}3HUATwvWA%NwAK_&o zsISf#-ueL=hf_dBFgS^WCMFrfK?}g(YC$z0WB7v)TnsiJxfnP%!zDoVGh?{dM=pki zAc+*X1VqQ`k6a8)pSTz}Ge8Q#rh}SojNv?=U=hO+3_9@@EX4rcVZm98wBrM`=#epe zk{JU7$10@JAW&0=vr(MWBnGMxx?6;^QGg=_Y3(Pda^egXfQG})LaD_12zv-%QA*v`3%c_90^FK zf*SOou_(A|NHpqyfk$I5l4_8ZDA5R7r^FZzYQumVEnxc?7#Ki3J5V%k34)10wm{;H zM!5B|pkgh&_X{lPb5tQ&51NbzMJ9%^xFQp7EF^Wj_yQ~MINOno1t+>r0e=#_;(tiREw!NWB00ijlh@fgJV?BME^bnK69rH&_ya zCkl{z7{kArGB9xXBPDN;n@}PI;+9pQL~#U39mpI|ggB6#uRyaXjN#`%2EIlz5Hw%| ziV6$^aYY3@P#|7p`HqpVKq?r+qrYPW3PfVlcZ@&*B}c|^JCNIu7m0y_9VJIWg4+nB zE)&TQV0Fkj3X)Uifz)kBQU`J|N{#}xp&7$Df53Cp6r|JyR$VE8XaYl`F69S2M=eBB zjS+#MHKB~*ULZG=A-Mt6wgW}r0g`hRWb^K!AMhNt4#|4Za!*h+Vi=1n8bR&_=V?%@ zoiSYYCp<^(Mlu$h+&TsLk$YnxF|-^73CGHxup9*{tigtZCJ`CKK}&BzaSs-P#Ld;8 z7i2&1Y+PVkbytY4Q$0?Ag-u@2Mxrdj=wQ-6r?bi`5Pl>KyG0S|M(jtXdn`v zf8aG0Xz3)_)eMl$*C_c4R30;ihk<<3jN}tg(%?kSR}fRTfz(|@QU{6 z^bb5=T}6r*u&K!T3eqHJ{0oo9M@Xtc*$5>XL0tsK@Fb8MCLp;1)Bp$Nt24w#BgoO< z0D%~o@E4x1-XmELHnvrq6T?_skqI{z+)oSN^B114{va6(>Kk%)3h;v#po1O3z`y_! zL(5m7f{ig;@*fw&J#PjE&P(uo1t|zWnL)PJf=z&wPXEl1DmsYve7JUaLW4*g`v+@( z!X+S?Q|>>kF$$Le6^4xAwf|wg0JsDs&ECh9fY{;0zzyxCz;%EajNwZ#B|t&P7|za! zLq`^-1jLTBa0yWQ1P2DBWB{#I!&veFp1gqvBqY>8(=l)%$i8;aqz0OCppjOTp;eHB zQH=xXUx;u&4BAJy7z9}vSxt{|F;pDmVi1&I zWMFy-k~KTR#h?R{RbgapKf=Xu0?KYa#>G$xQmM|!dgT}wgXnQC20?X32CniWTnr%0 z^cT@bhPZ|MJr@HAGyUbXbq0qGm;gClFrI^x2VE?agY%seSP7T_t5cQX1T|Q|0t}Gd z$bxLrDD4+e`T%89kQ$~-N4OYRuOH!J5I@SrAZX0U+IEzS;TVLCs7@jEne=-u1`uZY z%YMO`fgvG{jX^L)fKvj=U7(U&&{Pz~U0^Yi+{M9?3vwI;gVMR688@UT1PehLz&h`_ z7(kfmFZ&&528IbB*W?Lch7y`$z+z}l0gHj0 zf*Cu$$gu+{Oyk~jF@P}BU(R+U2Y_lv!Da!JumBrOk^|(Bk_R{>{Xh*8@RU1~+zBoQ zR;3eM46!G;7=+jvSr4A%V)%0s%tnL~XqtmDJpVlx0|+zyhlUkp>90} z6<-782n=u>>q8 zin+{KNQ@urGGmPO$hZ`8NTJ7+B&t_HP88zhMez#Qd%VPkAup_4@4FNYX(jDDnk{5H4E^7 zHETh|z?#J&nvuoen)Q$s3h{!SZG)c~ z=l~T1s}tq`uN-!Uih*4yq6D_s9a&6N32ZU47}(-x84L{GP=#OvML0M>Vt&YCq8yx{ zPIe%&m>55JV-~U)sBZvP7=lG1vKUAq*x3?LgkNWiAHRZKt^yKA+}d5K?4h-F=3O;l~*W`_efs2LAJ4 zHlmE~1t;dl_goAh%=DMt(uaXTA)SpuD47H0z%6k3qJnw2mXH=V$7fJU4=tNP4M-s) z5fm|SB%6q!9O?!3h#9JRp!_9d$qk-5gc`>HYS;)_iKC3dfy6-5dhDR_xd4!3?NAkf zM#Vs#AB^!dT%)a^>1 z&VfP%V>|?Rh=AJ5U^~mebCA*j3=HgnJ`4;GK*r~AaDuwWV0i`xNGvIfav=AX!Ld{* zz`&u6)RF)R3aN0Th=G%q7B@!(QlNp8jG-V$Dv}te4FgJA+(>(pzKeW1>mkQu7`z)9MYn-kQ>hIts&#sWnGa+ez<28sj@aU=(VhEs&>%-BJi z1v1zegzUsQkTqF=Laz}^hZ=Xpa4}!vVgO;}*6{>zBu#zK#Q?%gf7wCt z0Vp&njYfB@gBECz-L(10=_b`pW32&q^C)mkEg z94Iv&ObkOEj{u4@!RmNWoe2^HITL9P5EKQB;k%+37}$RWF)#>Zu`!6aa&UqM^kE(b z6;dD%BaiQc#6TvogQiU!K$-*u_&~EK4Ox(sficN}tI*-7NAfVJQUZAxxk7`9VW{H~ zKyfBm9ghG9vNJ(qAZIc#uq8m7G+y8s-u|A80fd?Uva5Q1*25Hn3Ih># zA#gpB0o4qu6GV6gI6>pGd8lF>pkZ2MF_69BjPn3wxPSmS<0xc9f(MjwmO%{w)y}Y4 zJ`^#~WG&eG8BmpA>jgQ$Rn0s!F;I2ZhAhSdab^#y7}%M}VjyQSFfg6J!NtIO`34t* zz)da&5ot!&%Qv|g1a3jtyKZqYe21_R?K{vs2xItx_goAh%=DLCGK_&CAe)Ur#2-tB zkwZ<1@C*Z*eQp40QWD?;O+p_41rRBd&zKoT8B2yy=D<{^fF;8ypgI$gVL(9-&JdvW zL5$%i-*Yj5FwX95x2fq!LE9 z!8Q1enQfFgP}+u|q%We(fvHXbOSVx!btXtJBwes=fF>VbaJISho{Is5nf|gHhA}Wq z0Qn%21DqXpLKT9lHxW4ja5_U41DVGTn!z~$(xfE72cF2ug?Jm39ezR$0A~lNw^794 z+2J)*B`8inUIewgKB9?%vcpYeF$G9=xQ{9Zb|$hI$e9cbTuW|qF@P}BUq+_2x49Tt zci!e=I4Z=zAQH#Oz$9^pi-A@C4i`h>9WDkDT}IZPyIc(C?tou!E|b2_SZy0=U*W0Ai!nI-ryTDuj`jEr7&8?giI6 zA3&O*wT?j^q?Dm;tpkcE5t3>h22Rit4RAJMfDCSnn22#;Td{#z<(cp^FmOrS6RLGKZKzsJRJ3=|qNjI1~Bb1_If;9?M! zK}xWYdC{luxfnp0=`UviXsIGJYCtmrqEQ?ipeabG5Cf?C6xCF~3=M4ojvl1;6v$_y zIszP^xk{K`uqU*5P|O2+LRk<+FGLL0JaCAspqd9(rvwT~1E}{wdnp;i^Ai{t*lS`L z7(RePGL8eh4&D)}1guG0fMaF`1A{A63~aa#M9dQ^26n76M9d#52G*-0z`(#%{(y^t zwdMgA!){QBnlrKf)fr2=bL2rW5A0A|S(HIvuwGkP1_s^)sCPiiR2akg6B!uTZ^bb%1b`ft z0ST=_s1mS2PLP8UN}*!l&~k=|RU(Ul4Cms1$i)D{$o+U*a5(&Y&&2@3On*5+X&sg- zKvUwPIh>rJq>my7%>pnnaD)g5fFlzo26i4a3&6zSS%85{@*x)k2qU}266_YP4_pi& z%=DKNlv-eVLEUE25>8G~;z1FECL)*^*j{KNf{B5Kt3W9pl(1l8U^hb(5ljrM7nFz? zxRf7qF@P|#`^>@alm5WP0K!avIYIFQ(+gU2AzH!735qThF=({G#K3kyqYWkob{{m_ zU}E5?1VtMI1C#DUE<|tiHP{uLNem2ppoMk~1#Aprt4vU~-w#|2Ak6fadjd4Cff6`lc=!h{1`uZY%e??90qGYde&AvNVWz*J*Z|uB zI%JM9Jo^I|0|+zytH}*u3$mrxi3%)5-cc>q!Q$Jupn0L*i;IEdT}t@VLssib?1-;Ia|dU_>pY~ zp^61P!ENtTzQ|M-c?61POwJECE}P!TpRa$V5P!1}8wZ zp?C`xekk4o34*+Z;sek?G{^}kJ^%@Ve89kV0h&(gm>C!t!z(^;F@P}BU+~JmKTt`~ zV4WBbBL`TJdkrW?K}{6~2CyK=2?L0qUkP&FU71<}qf*_wDE&l@LamH{1CT@ldCT<2McLq_9 zGh7UNx!4(m6&V@CAlF?p?Osl#7AoKBKtSGA@S7Wn2s_7L1JI$Chz1yuQoDz%rSUQM_{n7sKln zTnsE6%#7mSR&X%{uH<51Sk$Tv zEAHoFsN2uQz*4}(D8AKyOI2Qwl8Z)D~(qSYH(`ke<*1xB@7(iR{I1Vwg zGMz=Rl`g>9;w2Zk81#2?F>sVHf$r*IVuVGKc*I>T*RX;&7O`^kF^Z=_cNT#yO@wY6 zV*SCwDDHa}Nsm5svk+M3(gh@$!b?!jWNt?BRacNW=dU7#^;XbGKF9?iWjfHUL9C!y zkh;mm5Py@4f%POeqqx*9Bo5PUBu+VWdl1+KHV?TNS|4&Tux{mN6rcK#i{UPaqsql7 z{svjb^bwLw?IWmjK#5|>aSlWn9C-u{s^zSV;!k%%WsKMu#kn3sT@cF5DDM9lnhyFH z8O5hPM$#j?h6Q38NY5Q)J&I3|^vpX5H5wG~8BdT5VBQOr0U5CV36iB>kqlsfT62`2m|CcAQ{lO0mO7JGc^8nB@m@kG z1Gilm1sPTOgjt#S1jMEJwm3-&GKPvXFff@gF!Ol|Gf6NoFbgxt3N!O@b9XTEGcd65 zGk6L!^D!{6S~7wtHep7%96K*qQwJlz1vdi&hdxv#ryipqqY#^eL>?bApB1+?D+2?U zETf@tsCWc-Bx^V~0|Pgl%gw;R1LA-*L~uuPgJgMS84QJy1^AFvFfi~Valyt3AZY*# z2!aF<)(L?$fVp7vh4mOqxUU=U$p^c2pOg!@WVgMpb(hM$2!3=$?FA9OJCGfCv} zF))b3#X!LW5tD$M#|<)162Y~X1PMsN!oyIQfk7I^fjWWPUXp=92Cjt_qD5AZ0Tcj; zAOxwBL(|F%5|oE~73?kT0 z0|Of(7PuK0*b&Jcls`CN!2?R9oNzgCX5xa$!O|rI12=lmfwBQ|Ap$M}c;PV(&KKY! z1RU33S$Sb{{D6vzOuQ@N3f3sBfF;D`uFcr!yH6(#AgAf*{rlsG^s$I$~BNF_R`2Dc!=s%Qlq3YuT*S)2Ac!W! z&A=dp2sB|x77&4^T5bjgQII4g-9rm;SR zM+-L&Sh#T_IB7fk75z1~|cR zGcd@(y#=ifFH&^!*dp5$Zz^C{t0I@~SbXvb))gNlCKt#wdXAXl|u z&*N>cgX0Rhx&;fMRJRNaf}rq(1TO;vdPU31z#t3?Ua&k^H%3|kIa(B!NX6hj1Sc%? zG7Q#OlR%0CNo;W-g((Pb{YgV|E?Of^22!HSqLk=zkP;oejgFRqz%hIN$ZCuy*9uwmc?jo|V@!UHYB`C-We6g!A|8Jfcp zH8VJyVWeqLibd*&LaGuGq!xlGQiL%uh`}sF4kmF}*nwOi0ZD(7DCtiMlKwFIoD2*y zXvs+ymYn3!Yy#J=;L;ElU zD>MLLxfrANWnj>R)B})yCsHVaT0~g-p2%r|fk6-1mGE9aQkN6dUqy5{LA@JDLN2O~d7i78UHG(+<;tT$;6$(0t690%&{S%PvUB=1^*n2-z# z?M#9iuQn*HURzjYVF$6Ffx#Y) z3xlfw0|O%?<8MA*5Ct0O2hBPc7bWZGWF{4tr0ADq6s0DnFd#&bnEILd$h_jp;`rqJ z+}!*;WKlgm{o<08#G>R3{p6y=lGI`lH$Ek`BsIB2A7m_qiL9l#vRJ<~FSCL{zo@jh zBw0V%!q_s=Jjo>4$Sl>=!r0I<$spOpz{JENCB?+ZI4v>P46gDj^+uYOu?33AxxrIt`+ zTyaijGG!r|SDK6LAXsGU7Zl|urxq6@%cZ4dQsmg=j6||y4HWSD`31!kIiR>QFPS0( za#PU)i$e1u;!`U!OX4$g3qT?) zXc}1<7$EHzs3e3FpOc@Qm;)7pTFJt|0IsG%qD%}71&Mi?$=R8C=}>-hVsZx9PF4m6 zxNb%UhQy-uVvrIx28J}S(?JZFNIr~FkXZoM%*Mb_l$ZxoS6q^qoE@KEmRgjSlV1)s z1>!;|r#v+=8_LX01bdJj9!4At40-t}U}M=C81fR!GSd@FU|hIYI2jmhtgJumT zms?qZ{iXon*@5P2i&BeAb4oxKaWOC?=2b#j(2(I~V8{g9&4UPeHU@_L0#M5esx+xI zGbbgr2<%^O28Pt4qWmI|6f*-uUTH3f#m&G_mYNJ=ae^YV2pY7U3=Gg}A0)*D_Z=_D zcX{!7r8(eu;sC`+Nd~GKJ_d%8WSGmKro@9XDM%SV14Djsd~pfb83GIpi76>yCJ)>S z5UaE#z5t?x1ExMcsj?&$>=966%CATTM;F4K&=xM#j$|Y&a#Kq(@>9TC1sNF9O7p;N zPmn?(28P_ylGF+in~i}XGq0p5Gp{%^8LXCzfdQ2LL2{g+1f8Fo2@WS=Buy*~3^|Ea z;D}*{dq9MNAvqNiK3t%LRUVHB;e!0k;`}_2nJh@gh%zt~rNUjp!oZN13XXOb28QJP zykg|QNUcarE{QMBtO6$~F|dM^vP5tgilXq?Pw-48^ISIv(UVc96I8(!ml^ z3=Fx6l}V|f6p)t*_Ld0T1Zf6_G;m-eQWzTp1Gp&*G6#|*p)7dfMW~elnGH^9vJ4Dq ziNz)HkZg)5NZ4QnO?+lv8n~PS6+O^$ii?3EH#Ijo0~~m4a7W1@3nMI(XJAOpD+4)( ziGd*}GpRJMJQGr8D8RKUAeC02^aIJDilCSP_jy3N#TXdUauQ2QQo+G24Gwg04g&cc zR+@rRV{SnXIBb|f#aSjKmkBX2Bqk?AvMwm{ON)vjvBC^azNtldi8=9*A_Qa?7Xw2| zerZxpYJ6&53M9V87#K=RGQqi*8Dw`+aVpsVJn&S<#=uaT0uEIkkb*o&vS(soNY2U6 z1IH;B14B}NQ4yqYL}Wpbjfixn1d1F``voMd%)pS8lMgSsRTvmbN(*vehQacg3OpcH zKov$&Q6|J~q6`fAdC6eapsbPzOr++0#x1eO3*I-u?)$Ts9kgAG)YW#)kso+<-F z5tzXQOLw5M1XQkoGCsIYVPRk>DhIg~Y7T;fNJ{XE1{6}zuw-FiC@)E~fa(GlNn!INbXW#^_H3d~D49pL?8CkixIBZ!t*h*M=*bG@DSXr3+E1Os( zSvifF;{;YEHVzwB9*#5?306%u<}OwyJw_IBR#xVntQ>kQGOUcuGfOxWn4}pP8Cm2Q zIGGecmz#rj2EJinWM$=Az{CiX0S7Wx83UYp9I(pBBgfiKenwUfE@37%kUc0ebNCrq z-MAQ86j(W!&z0LCR0P0Gg{ok+WkZsMyAvY2hDDK;hj~-64XYB9g9s~+2a7ywF!MPc zj!#THIIJvTQDW6+{!?hfYR=4=#;VU;J^{qy@B%R-*qEQ!ailTNWQ<^C05QEl2C^~F z<)6UBfy2OSEaI#j%q{hxq$tF^qd1LK#F~|xc@B#gG|Fs1vHOJiI3tGwI9548D!JL1 zQKFSaoK=MRIjzMLS(xuLHU4rI!6hMGAl3h zpE?d3Rsl9pQjBB~XXR$z%FOWzlyX^l*fPP17G1j@t1KJyJZ3Leep^?0pU!Hs+oxaQLw?FXW2=g*EdZ zhBPLAY(dQ;$jZsZJhv!9%E7#-5J`Um zYO*Y0QDqfo{!s`D^9igXY~HL=9Qv#xlUc=Qvx<06V&cT+16DUK8&+Gk5^`l()L4a> z`^s%t&6v+{*s!Rxaximy;q(MNp@Y&iBuSgIF>{ic&{;HCIk=d=7TGWfVf7jZ7xPg@ zT#{~F*H}52_cHEc(PVDqNMjXY{=;CyqRcAFyf2$cmytz_m4o?6wHJ#rD-ZKt7B3b} zRu1N)!fU|lyx5pmrs=WBvvRO8-x7#m4Q4)Ehbx7E-MF?Gw=*WNXtPQ&e=fFRW#O0{ z%__xQ<;BXvq$&YoL6ToD50g406BkxLx^XpuQWc91s|Xu&W1$T=4SZ*aU=?7S!2G(- z3nV;^5n@g|-vky_Rw?FRg=wrJ*IC7_Sw*}-sg}8wFO5Z&RfPF(VH%j<%$G)|C-RCwW#5=EsZ^kaIjFCo8jxvN2C(0!STvarihGfl-=E^i z?>1a)%w%NcV3tG97kQAJ0Sy~Q786zp=C9>8pjIf($bmF!4%Q-@12KjH zY7XW30csx1KpQq2R$F|@#gtW*`FXJos|X^;&g8+9WAWFIB`gB0^2`T{K*i`JR(VLP z>i{p41|yy#K!BB#`4m(OCq&COyjpBn1X!h*KNUkFaSk)2-tz+G4g!&gugO@#V#X@Z z{JhA9jrmV)8gmO1sQBi{V3qG<72xn;l@9~u`aiWCM6~=s!Jy526zV5!h@W=j^%MSZ z#V<>A&O}QbB`l!AqP^ILRhoGl6R5CwSqmvFt_y(*3pM5gh{EC?w6ItNFDwot7Zw;s zli3VnF=ORmURi3xe31cnvPBCH0o2Th)|4_pl||F*fKyhLfs-i!COestkyV&$KBz*+ z)u;ohhlc_Y>OsAp4KVXpFfy`gaoMt%vvM=dAc2bvdaR;sHmm|RtfFkCtO9J%*3rA# zHLR?WEat32%m*3uSjCtxGnBv-im{b~Yd3T~FJXGbm}fG9++o9tq!fvXV$)-=E+!^e z6#@^BVd}qze25R3hpGP{zGGk*uKo)sfcTL4AR{BIB-cb%4mOSntgLJt>4;_!LD+}{6?g?yQ5jN)i zB~2`#k%C0TwP0Nk%OGfyjVeLVLGGFtNbg{Q#%aJ|M}ku(8T=G2gG-#iGQ@ z!+etQ6AK$F2lMv2c`O#7=D^PSYb+9M%o`c?*qT@zK?8frUaZ2*ADO{JhRlnq^;j&J z8yVNIG0&)*!2AN%4+aGdJXt+wWMpAtRpes+#juM-mX(KDi$j4`l+BBk$BTuDjXBH< zV%5A_m{lwmtSrptO<2rKD1?~G3<*@!xM)C?q-Kb-G0&_A4{|bq!UEdMmSEsy`T(s`L zD7M&`-!ba3@UjXpcQA6Kv2e4pFwd*kV{u~TW`0xK#NrH!zc@XRrEJVM1#MWIS$UYB z6heZfu{Mp3*{KASQP`OOb4P$B_m!Uj=b*;gG$wXXG6MOe0NDv@tj=7_vxFHzNrAaD z7nT&%SPhul`1M#ES*4jLd0ogEqR!J`A4-AZu?gYrL1<2B7 zth`*zSC|>WeCPbqfiV{30Bv{#(ci6gn1*QSDF`#G%G(Fv*rm_c3(74 z3ovWhu(B{C!Z{5Z5?sS`60+GOYyN;@Gg}%cU9vIb zupAsBs3`=^a!}oHQvg&~V6PiMoAg_VbSUp^>mMVV95*qB@Ekup0-az-gAvx6iNdEEfn zotCTuT+F@oj7*^AtSlG~6lay^VxGmtp~ou3*2KKEWCE)^^J(r~tU_y8#V0`Gp|k7> zT3zAD%FX%x0h=D^UK2 zPE?7riZOrS0!=9JGoL7r0BbqGRstUIe_v?>o&(xaqX&+R^^6?znD}6M01{=;)a(kX znDQ7|1z~e77#_7|_C)xb#4CCm`)?%#*6qz#TQvoFaG{@IEI8WQy>8WeFSerkYQn!s!Y#Xzm3m zYCsc{KN;76r)=hdO#^F2%psPrLgx@!WujTN{6X`UY|J-nK#|A82ApXQ#x#=Ny;9`kALc_13%voEYn9w-xwph;7( zY8#MhXac?h8Xy%1Wvw-J99|I9Y(Qz7xwC8nS|SE3n_17K#0Y6Hfl6)`=56)!z`^o^$2`a!87P}NAQe|!C7_y;xQg-wC@VIBvLcfhwnBjm(o!R-CP2=D zTwc(M9yBe^0IKK-@Jk>a3Xq?W&0$ev)#hT}Ahe6sp3RHJ4Ky@=fTmYa9fqioA*P*T05|TLaVUyl zQDb#xej+>%Y&4-}AEb42ih;uhGMNPO)dHlLg8AwiHeaQ&s6qTC0c!qmoZt`wMJpS# zCWivn7LXUT1q5+m1W^uz`I!UU&Uyk$2Pc@%*KvG8xECcBzzv=y$PFH3X)Z6EJtI(1 zpJG_UHjnvyogTTkcQ8T|86^BsT9;_*NN(mJ>qqa+l(4{h(%?4wp)yd9L!6DdSp*~l=~IIO0;SzF z0dyw>Xwe0vSq$o%_j06xx+=_PIJ|J02$I2JBC->?7}?Tb$sX240fil?w+eE~DTZs{ zghFmR5tKsbks6wgV606?>jYK{E1cjZX9S`QGAM`m z1e`va*vM?$f;CL|1!O0cur)!eka<{Z z5Y#?25lsV7ABM;vMf3n6VFZb7E^sc`#iGV)$Hx4R4^k0;BHfIYm-z}a#|aWgJm?Y2 zDDJl*RO4ayHwBr+paS9mP$EM&22?3Py6fl_5J-lY>cWdHA}vi1RJ^h=e_(*b$AtPc zkO1=s28{9+(O(WgauyfTfC$8~@Yn`rHBtw0Y(Pnz1DxSN>D-2G7i9SnDb;=gvh%z^ zIVA$r*nwt;Ygn@btU(1TuTC)_We0Jrl@z$31)EA{#>ey~e5~pKlJi*xWIRNn5)zZ3 zWQQET1}Ng-W()b91?ZdsmWWkjs`LFEgKEg?Qp=HMpP#55$7ni#P`sA<98L9<~T*=E6H(%LZDt%)rKcih%>vfy1nI zL0J{lz_i)Le7+9MKxF;{RS@q((j>@Tn3XnCO^J8B6EYkPT}lc{fDe!jC#9ILMsidH zTLkm@I&h^7YB-Qu^5Gl4hS&m$c`h$}o`=N=;kE?xDTZA*``_SZ*)CA)gc(#rgFB}f zT{w838>Nd)bk`dr|ANY1c*hV_d4NXwAz1~K$PnGs2IOF;aL@v@AdQRpZCwP52y+*s z7aQ}9Iz3hiHs+O#Hmp3%oE+C!g`43GlU0a08{{YfHEBTYShN6}0B*;w0rj!hfGVI9 zCqOZWl$}877122Z=Q?nzC#An!g0p`Gj=fV1AO)CN4LoNB?|nlG4%9$K_8_ha3UDhK z7E>@okP2R8GjIeF?o4 zXh0Qxq?sd)`8eY>+(XTvA=RCApj9`_S3qfPP2B|MD~ueVy&r4pIMP6?f{rsjVdmY% z#=NF34P*m&(|`?R$o2tf+sK+a(2y+$8}lyiG!}bSapp}`OqP(jm!s7Yka>7ek%sJI zN(KpikykG?bAtBd+yd&_!8mkkV0jo4y8jB`qZ2F?`1Xg}FFXmO5 zX&@`F>w;EUMKG@n0&k|cqZ`5E$11_RDs&C20JD%D$X(0};La*x@nz*|OoDr-dY!S@w%hFiXk5ohf4yGqvM1aXtNE6 z1giwI8k0UF$Qj+4O!|y0{va>>=U_5qWEEkvVF>^wtS&w;Rw?HECRTxotfHG)rI?IG z!OY#Pa!l%?tU_!|CPFOQtg39xcXF827}+9NE!miPIGRAZnP&=f=&><>&p5%N4H9l; zv0?FNe8>Hs(%oJr;jf z5$11&pb*w$6|rG%EMEhYgP0GJVg4m{0@PGtKAHv6$-u^Zunx3#hxvk78l*jd5(qQI z;ej9t4TKXJAchnh^KNla5P&?q7TLqXAP);bJuJq?JRjY|Jj_>xIrKn&Wj>I@p#V-Z z=QTky7fmeMtPIR+g23sBjrqFn3D9%~D-T;XO91oB%qBMG-5F`D@@&j|#5sh(zGw#f zf|dE9=madT|04=_y)4Ml3~bB~a7N))UR+Tq2J#Us3JuwqH|AnQ;SnKF5TD7UBnr2P zL89<<5h$Wiqi~BD#C(tp^HOLO&dNrO!WLK*1|p~9Kt6mllVD6dqPB+cb80H z<%tB<&CD$|9M`}*M}9MVA;fmkQ3$lK{RsCimOxfk<_`rEATz2#$R4-`UezTU$tum} z#mb_`%E)F5>9v5?f?j}S>@$p@xVB~0VveyPxFD2S6*SO8-jYye)d=vS(77Zn3Wewr zWB$qrs-Qq+8+-*R-esZ8s?ddDAkV_9osr<#1vL=wGDAE&5ge(r7(tr_(pc0$mCt00AJHndo z^Negz#S3cF{Hucd7~}z>d~AT^V+J-GdV!X-FhAw8VUcHLWxi0li$xt&wQj6C!6X5ytw&rsZ>U2_ z=fk@M3#f;B_5!%L_GXo2E(EnG!Rv>a1vt_mZER2?SU|i|h?xuw%!Ez-Koe6P8X!(! zJ^}WYFqVBgB*+BdG%W#D<~pdXVBWzD%D$kY01MQd3Jt0eOuPA{Zf9)Xg}&y?;07d&nCpgB_1qC%f70@N%%TlWbPXzC1{Oa_h6 z;B4S$1Z_FvVqS&5O&7c)80AQVBW#SUl3W~XStMD7m?u|rfCg(=nA^b1NI00UGH?j7 zNV1ABuPxVO1JczS`S`dW6H+d&oYlCn3aQhU(EzoF}7WxeYb4PeG&@T)PFe7@C2lsxkuszc-tN*MunKKD z2x=rVZ(|2{bWT;+fOg3K=ipFa6=#Ds7b9TZ+6kb&;3t^Bus&h#sp1e~{=y1!vH%*VS>Pu~;#8v2%Q4v1eXY$sxod&b)+?;~IF} z;YrmK&|nR-GHlTvX#C(!HEevh31r|W7I9W9Hs*~Spbcf9B7%88CsQaRt62i80~?bL zC^9+Nm|qldq%m)2X<~6>?yO2WIUgdO|3AAR6gZV~X2^;gt@-#N)XAGcGSr%qI zDF(FM>=-kX10x%g3e>r;YnfCSp|(r13NSBX2QQ#x-pdS%1S#hIH4s-HWCkzIVP07J z3GA@@To4DHsd2w)O)JESS6T`u-Jf>3o-wxYyw#Wau7%- zddPvpA8gG$u$v}eS1bui&;pz)?B%vTseOV$L~n74y=%kVI-u5V%qW!_f}%CZ8?(^Ygjp$uT-A^n|%V5DnP3~k&_VIKu|x}nE3!x1lYY#DnNRCm^46z zuK@FFHqhAd>UvavgB%C)H+Ve*JZxd%h#DB6oHv~nYuKWMHz@iZ)HQ))a}GNs@SaqF z%#>hW!dAk{!Th3X0;>e`4AwMo2&||CCw@?voj?mSj9daP5I~vn2{=5~fWiY5;K<>;NVdMWe!-<19^pk zjk%{59MIst5eq1AwzKU*2?&tbbk+zgVoe0aa0j9+^C`wpto+`vY(Ie&ob5Rjkn=q# zJ92>XJ?OBOPF4<3#y`P4y&?@H76A?kP(}cSIY%0fK!=A4B#(cn;+O{tJV=}@uW=O&T#{sy2 zT3sIjRt1Tn2X(Ln%rOBeNFliywQRsI#=yq>wY&+`XxYXN%OU8_02i}sz(oZIsQk2H zHe%9-Y*O1<&jIT5VQT;EVgAPeN<5&0dP;0T$BeM<0{aHEW>=ee zHvbw{BT(55N=@jrmJ$8gnlbr~+AA0|~`xth+!b&`7dcvoRm7=pf&vhY|N+Fnpj!OK^4hnQ0*?m3M$w|*f{1fnFuf$ zf;y9<4MaBPq0>NQRc4;Y2&%qWUX3;rDQhO~LTVuyQjKXeZjBwG&|lAf!R^ z5!8xzW949mb!c$56DNS1m&l7NQNTg$U{&(xru{20E_>Hr%*` z6*Py)yrZs!RgSrz5xgm$l>>b80~_;XcF?*y4(7cT5v<$>;E8EtHfAZvN|~iKpg9;3 z<~z)wnhMmTb^;FqQ=U?#=M3VdPaaX^6cn+Rz_9@u5!@6YvzfyHsB4wS8731 zH4My%ZPyU~DTYrh(yTc4x`P%rb2I;ASp!~rx|9d9cJp>IXa|ir^E^J#X%^DVPYNL& zg@$U7JS%e(D@Ph+;1-Lkd|A1fZ!_C~4LpO`f&*%?BW>vcSqxrL&C1Q(QUf|XLztNa z>xCfe>_FBFG(pFWVS(bpD#+Yg4GI)dW(i0b7{W#wAzV<9fOZOPVc7+CCJ7ef_IEi8 zev3gC{$u$>frSmIK5YfJm(Nv$wsnA(z(SUn<6Q#l1x_)&Olxqa7!OuK=1|a)uxc>>stLs90Mb(KbHa|SAnKO5@6bX@Ij|THX%=k_(Bdq z1(jrQr!5m=WEJ9KZYjA2o8$$9l@SnCC9EdQpBXuz>B5A8lc@ogF21ufvZ`=# z2(xm42VJCD+1Z$%RF;6w_CO?K786!h=E>~4*qGZYPO!2t|6;dc5@ldyWC=rYm=Gh2 zFX(WBH>Dgx;H8KQ%R!6uOxc*%Fxjv=!PXx3h=A4}1~Y%jPGj)}Iq*vDC&+rk^-SR9 ziOiiMpaqDE%s;YiScTcvFkh&h2bxhj#Poz!$(vQclT~;(c)6hj8*`=}y1|;vy*Z!> zG!^F0;MIiE%)L3FRd7Pg6GcELw;?S^1Rb%Au^{nP$Qo8B<}}bEMs8+gw`#J2_$A<# zkUz4$z$+o;*qCKOZu0^yS7c*uVFOqEEJ~p1wI5Y_EMcsy%xm~TvU1FKN}sUGvU#yO zvl+4agVqqTC9ztw*|2hhj*r%76=R+c*;UQF9&{ukXjspejros41d9@@IP;mL2`s*> zhRpL>*MJ9%cUCm9__B&IU*xr66=42e3GyKi^Lt+C!bM(C#I!*2H1kpL%yJ|vE5~QZ zI%%*hM3MozM#dJh1`Z@+0MqxMkrA{ohKpH%V-4(F9~(C27d3h;4xnMeZf*`C7G72f z=Dvz1)LEDcw)_}GoaCk9+cL9MMozM#L5A#xHMiybv zQ6cwhIE26z%zeQoRzcY*>U@rP!GB^;iYG*_ge-)jk{ZO~DCl%**pi zSi(U&;vccjV-a8#W_IMTVU-0P3NO3{v=EoW7QDqI5j0uM&U~W=+mS&LECQ^o%==ib zflsA+1Pacd)oHB!%Wq%nxcfKB$bXv&? z7SJkQ<_5N1EGn!#%%5u`Sd}NT>M%d9nZV-BD$Kl+6*SY#&3sQ_0_YeH79%$1Jy~fi zn#}v_(;&_PIi-n3g;j@*`8tCas~qzj<_WAi%ui|}SiD)~nAfpF&OcdGoyIl+Gzp5d z7XzFe*qB2=Z7igtgg{$Db}@1&un4oVFu$ywz+%PxmHPw>FDobW+H%l|)tan4%$tQd z6hHyQ{3#Q(N11PG&&c5gKA!vn1IINMS>~^t91*Ny z%u^~j6j%kAcQS(}TD?HFX@F9s2pjVu2FSsZ-xxWBScF+cn7itj3>ev%_tk^lsKds5 zkUb5Q(x0$+gOXAQTN)elkIE7jSx|sNTBvam5PWj(>he7g!X#|$|GjBOVvh}b~m z5{Rr^(1pkqph}%5f>nUSn~nKwO%p388}k}wJ(f6D7UuUgX{;PeSXnp@vRE-+=jPC3 z15Io&urY5cng6X zaexkK<7GZm4sIwjKda@~#RfVXft!uFm!pX!C$KWVu7vJw0L9<~ES5*Gfm04BErEN( z%;&hgV27n`D)nO1gOvkVlMO5LQ>JTd%v;bC45)g8c$9&84il*BZOh8~6uh<^!l2WpuQ0Cp%*x7f9lV|&*^mnm^LMkda!kaqp^+6@NA}f5fa9ct z5pI-++e{PKn3vXaG=aM3cbOok$ROLgij|Q? zlvSFGc}+EBCoi((7gk1=1W<YBWn{j@Qo_dES;Zm5A_{8ttYM5`;{ca)Y|K53 z98W-^%wOu53>le37#JCmeKDC0Gps=NUCH0%?6G4Db>4R@ty{18>N9-3vEm#ER3vz%->l+`ALR(5qBC3 zBP$E@qmnhO0@*B}lMk;{feu3xV7^}mK0J~6I%68toHZ=mtRl=;>h#!{r_}8N6~B;* z3cQb`v4lepB*grm+lz&pjd^?7JaCR($DamnyF+Zz`VC7~5wRm}uPm_d)2rCQo zDFz$(k&>X&a=V}&sP&Zw3KU2Ju8c@H=nOECYbK7q)y6Ce%x+e{N!KqDJZ7&+#F+Z1a++iXCqsiuM&r>p|Zui5oj z#U`*Yg4TAgtY=bZWMh6)0h*R)W1h$k&J@hcK}RBi0^}wO=q@499){)BOg4<*K)T5S zTJy#HzJdc}B?}w#U8W{B=Eb#7Siq4#mz_zQ5q#bhayDAe4k`>pxR{%(7+HncI6&de z!p8iMH4WUmM^<`=9hz!4S8;ejlN$$Svc16yNwx>UvBbiBq;w4{PbP~q^Os6oiFOxq zqCJ6#46wT!OCTemh(rq+b-|Kolfa2)Cl@5Wo-A!*m0;#eV>O2*)Z5h@3ScEj36)if z`FhuVuNbp|uIagLl+L1T&Fr0Ru~R5_V>(pb4bDOHI14<{&1axk}+fz04x zK2m)RDM>Q2GBC%1GtNtPCM8Bzu?66S+yM?EG3Fnjgxt*H%zU95nvmI;<3K5ak&XEe zJCiXZxT7?;`V-bv1!|y9VC6UgTABw+VxT4xlNKmV2r+M9=Geu?Jh6gf7pM#MhP?@N z0}JyR&}v0cGTOq*WDVY>!+fR+bcP&M%!HA}gVl+7aWx01oiD(AhXu5)fXP@0R1UB) z^E0V4vhuRc1DVCfyswG_yWuvmiH)#9Kovp9#>q;-2(oLM=Te=|Uo zFRJGNElL8dV6kN7U_MxkyVEIJv&6-opR8^ z8;~|@&<;ZeP_(l$pJxN@8xvq&TYrruiIs!-K{*F#DYF>!VNme_&KH~OK#dnXJj`F%=dm%jmPauE=7@loAXA~yG@p}^m5(bPoS@HefIP>;ytUkm zm6cf-)KyFYS6B-xAYqSLVSx|(1=m=he9t_Y3sNOLWi3IilGvE1Re+cHfy-$&a7Gqp zW5!U&;>>)xS`Qr2jkR8&{J?yYISpJDZL0y5Zi3A3IM#qFpsQsZ6F>(Ivw^Nt02j$y zSwS15CNNK}U{Ype4P$=I-URn9#J3F0r#YbswiKf|_Z2Lq3L5rco>~E_Iyjh*u|jI- zT~%JpI~YN?R&X%?W6)z2WWEW$A&H-PJFgcT^Qufxoy*F`{Daemjk%*tkNF2DXg4Ac z8}of0J@Bce$VKr_uua0m*u=uf%F4WrwSr1gpmV#A)p3BkoZvLV!UU3T1g$9s z9ijf8djh1*^8`nmhly2*d2eMJD>w5kM$j0A7xVv`G&bfI=6T@Mv=zMn$d36a9|vgf zC;@b(4?_3$EAT11RL`NCRlF**7ixm+V)8UwS7R276Xk(M6fYmtOM=g=3ryK zEin(W9RL)9E7Q_IS9`HB-(=uOg9PgGddTPrSSiFyp!=5inQy1-fhW1&)Pc<9VV=SW z@`V}mQC?7L=3ssT3o{lqh|wTn5$5ZX;2olD%=@zEfwqHcf+Sy6fo{G7g&7<3()1?K z@#;H47y7X=AFula%eb&3!a?4L?n}e;z8+TZV{iI>0!MaZtqmKq6H-$I(k_HcvdA*8 zWnTlXalxCuU-5z4YpXLshr)-kF~0_tm{TjBurPtj(V5Jk8wqPl*qC=QLON?KDA`~y zBe)XgV*bUz0gB}IdL~mwRw*{{IrePK7a5wEcQAsIh!FE*Cdk$d8x|&3E9O&0pgV}f zncJB_!OhS7vli4C1T_o4Gt7fT)rnfrZIVi$>VE1Y%AENFXtxRzD-ZL8dM0B=Ry{UQOOl6;`2+*#sw{ry-Syy-f%zx{DCLTP_^dn- zt#4pjr+~HUvVm^*<6vWc1JcSoy`D*rkyV7bh(m~#hsj15Y+F0%q<3&8-CoNS#R$s9 z7Z^E&AP0P-1khzhMpgl?Hc+Y{@wzWoR<;P{MRiTAEQo8qP>k+m0-ZG?#KnA@{~8O( z>#YSPEUBz4%sd>R4LivkX)MO9Qp_{zL1&x^G4BFh$*IoD$9%AM7b{OXtKm~tDYk2@ zVkcOPS?$@Fn`=R5=BBa=Fu!DAQe$LMXBA_f%)|jo6{5^v7&w|hUYK9K3$(BE5ew)- zFOZeJwM`%^r?YY}lVcwnbALSt$ngSf%pVzdu`&18fzBe}W@Emh3mTpVouH7(%E2}d zyrbQbm7DoTa0z594IA_Bsx_dU@G!NYDF~QaV@L{G64V6p2OINSX3*d=ia$UXv4Jd| z2eA}A ze7>#;6i#0m!QD$C=BNCiRLH@+v7p44Rrnt(YdIV9$J!?>>YzOKfjNzhc|uJI^9SZM z<_R^RoX5$={DpB1xCTefZ?nOJGTyALyCK{DP!hyKCh%$IT+Gu!dx=2DR&_C*U}0jF zXHMb}V%6agVF8t<>+6_|K$D^z%s)6e6j+!*_qdg(vC3t!3Va58W@a7ePFL`_FgS^W z8j#>zIFlK?jp}&~sHWp!V-^Q>7(|#SfX1moSAMjEc9N_^v@Su#GN{~TW8TXY!OFtq zAk3o1YR7zl2ejjffq8RL1o8ok5ggzxPHdRR--3^I0W}#R2k?Nb3SYy*#LCLtz;uE| znpKRszy1?s^cN*OmN9|y9)~SUIx7qF(mGHS$S~h#1n-4re$581*qT6V{@9p*3W1AY z=DrNj>^at*!5OR}Y|OtIC$Q+SN;034vSG zXIh&1WK9~YA@fdV&;f`n%rC1!>s!E^r&)QJxh8=21%kHPAww7U(|H+#m^#464P zp5$U<{wmsp+J<2Q)q&5MIJ{UTCbIH#l(K;G_EUC{%{T4=pEfyoAI&1}m2qZm|EiZkD2n7}H+ zqcbn42XzL(@g~E(vR)5lW`C^>Xs(ux`2=$ltH^w?`qlLjtRl>n;7VgMXt-CDjrl8+ z7pn|2Xp&ryZ33&w1Xh7vto9tx()D;9_!gS;b)YgGxp4++qJlie!N&ZPsfksHEsePy zvRDf=BiaONO)|<{jY5P>GFs0yC(z<6*X#05Vn(WULIc=R9yxyhReE zgNON98l>o6Q42bI0@D&!N#=@AtYXkx)^S=74tNed@NGOwpmU2(mUDobtP0FN9J^Q* znX5qgoSV6o4b;r!V53X@p$D!%Acsew)gKY~>krt$6HMx$%bx_9|5ifk(cPe=hNv2# z8_KVN27H-ao7kAI)PTxRa1Ak!jrnOAs8J%q#ypj|iH-SnO$mqrX`RF(cZv@QGqPlY zu4$f-XTuW9{88{4eDa6|Md3M^!WM)=W?qg7;2u7z($~VEK0b7vMk)g*lfX2{fCjSx zcwHqIvjPWb_daO$8`3pfRuAd}Ge2bl&E^O&&!~fJ$6Q!{jYW`^hxtAOXw9Dxb1%Cd zizf3KKJaN>EP|{I%&Rg%O#==#=9TO=tlZ3pYe8G*Y*-xGm;=&SxidfuSV8qF8}t4e zP*MY}T?OT@<<%1)(?&-bn!pPT{8^3Jm~XO!3JZSb-xW5X1zw<;D&|=%pv4@HtXSI@ z&=x4@V6J`8d%a#)gBH+&&l2FUft@=7nw#HV{Rz_CK%5}LBFHMh{J44oxD&guz6s`6 zR(@uWH1Hx0sI!__lvv@;0;LbIhe0Qj@PL-FM6w97N;5yG28Ag%^LYl)pfAczW1uE6 zx0c6%3XXVi5#ooJPhIEUB!F%$vZsae%6F++ zB(UOp3M^APOl9yaD@RrBDHAp~CS1isb-IWp9kx3WT7>^sWgnQa#|6M{?t=QK9vS&X1# zHc{H@pl}mrK3xUswPJ~8N9F^JX%j)qSayN(8XNO#X2{Uf`dY~7?~?j8piVXD1P%pO z#-}Wx95aFC$wU@S=1qbTERM`)^EiZ%dDlRk4In92Ip(&kYiyrD4aSo!pCWN(9*_q> z$0dPF_$E*Z4@#XpY|OtwC46fQXeA``8|Ef9=%5cu_L#v4>Ob;xG2akc!^*?f1WNvF z% z5;QLHwd4e7TtbZP35zUfHu*2}Jg~akD+;l-*CUUSL9#=O0n1Jqv^VdlIBD*3<%Re(x98*qu*Uh)J| zqJmDxMoh?oN>&~==6Q^}z)kmUHK5{6l=%$vCsu(-P#$2O2^JD!egPRM`B|+8s^r+1 zyEs7WR6&J0_#zz^=HHp1u?}$a7u4WqUJAM$tFeRwRC98$G54@Q3W7~F9H4m@9_HgL zpm7e+0!mQx9klI~0koPE)EZ$1t?3tJWn^9s9^>L+KEecAi=hXqdfAv)f#!`9Sy`AT z)Nz2SCpG5p%%I6M4(5;SOe&zc#QRkoAhj~gD?l^!paEE0(76$*tTN2bOooi$DUdrN zpmxqmu%qSJm~S)av3&wJ*#$wJiC&f_R)%Ia=1~qqrz6ZgQ5x&Q49>slfYvK zWvq(ntOBLrQB*|^AvPvsA?6dcpo!vC&?xFGrfZPi1&Y@BV67so99LOcIbyJ++LKI> zWWb~+#ezQ#fP-v%HR#}7B4eKg6nracLG1uFX3$Bapu1BKGI4xjF=Mr1o?P<@G+7IZ zI370UHH^Dh%vklA57vP!z?T*wmHV?QaC01#Oi4+QC{Ai)0kt=Hm_IXcK=vx#s6kF7 zX<%n=VVnnZCIj>A+B8rKW@ENY1Gf_IGM`|HV18f2p~uSN0&2=K@2Cf@`vvtWrP!Dc zGwZR6a-@Q^FmJE%0#8#T5~K}i%{&`(S3M}L^D@t40gaP`^BFrE^Nkv?G|02ei|h1Q zWI^@PCAMpz#L4`p8nnbylDU=T3222XsKF`6#@ta=0#95FY|JxQur=XMfrmlov&b{& zFzGO|q%w;z88WglZ>i-_0Hw&YOc9XbK$KMU8Jb9#<2VS#4!I=-Qp9L7_p^X<8V~b! zCeX+_B(XdJ#R6#fQ~F-CRPRJ>r5PatU}ChD!o7rS5|H|9Z=5;bj)q09%v)VDKXGOhJ|dq zSh<-sz~g_cJZy%nf{|>@FOy0@#)4X9u%-ZLN`#d;5j^wB!@N$ILkLvKugnCsT$rzl zr9n0gp#;cEW=57UP&NID2~=)dF<%!0O)jLda-^|xd$AgNf$C2-=ACKS>gx^6X#G`q z0ocU?t^H5bmaxb(KV{(9#UjW&yPnAe(pcHW1U<_S#W9mW)8Jep;E8n9UdkTO+Bz=g zoweYTeR-JMSvb;I#o0hDTM5ui;~G}MwX8z@;FfiNjTdI4A3pM;1{#8V$^<&Vn}xX# zG-)BoD#?7E0n|~M#LD-PmFFj`)Mrp##=M;w(j7U&2d~ON?Q7(!43v>2nXi<9ZXNx^ z0v?C~-%t&@+4><9hY+Z962U6Re1dr%D6O(Fe`f#=vV?;UVE+M% zOI8l%btN2W;9UeO7@JrnIqX<+@rg<{NK81+~=j6gnSz5$xfRAE(SK3jJJywL#E zTL-HES5Iwqpl(0dZIPhFx~v|lfdXE){{+;B#&YByXlwwnZr_ACIRds)AJiwYWj@33 zgq4Bg2a5`;DDxu5d8~4vqr=(28x;guAr1g9_TO3yid1k_&!K>&D1FOHs0-b}2JJ#` z04|kKzlPlE%Pn>peFQ}ng}-LzeOD2abPxPJIEIM zI}D&P6BgzVEKfi^KalbkE)E4yDOv-{cs$HkI6=*L1~z6$b4-qvhGdGnD+L^n}3n~)v^ddm{_j276aGz`? zc#Z{I=MB^|XJDRO0~&Q;e#H*TMy$;1>OiyoBFvzQwz;MCnxZ6=5r+=fm_&ssf!F^NA5$q%;3x z=78S%j*`n4!BqEGF|xQbUtoZ!Ud)QE1GN$so*$9Jvo#meD{B>obdE2;JI9~M3C}~& z@SIx<>D4}lhUXzFgy#k}Pz5%hg^&3|B?su_v?eCd9KSwu9}9;UcrG5iHVR9ONY8-JYZhSM$P4L@o-e`{oHyaFIgacaL+0&RToa8v82y%kk(HHeF{HNul36et zQWY~}$Uwvy7%*iv!1VkBn+BZ|1(^odLypW8Mv&W(OgjLx2SWyK4~EPIn4TskP!<;A zVqU?*2%eI(VToqtW$r76Uc3Zy?gNZ}~l9nm0_409k>GWWw&&PGzSKdlXF##0l^JySgEwfwcnN4b zNMAi@=N$|4o;nUaY;OJpHSK5}Hb*aIhQuTIZgv!V*5Z`e3T_|@L-sDA=-G=?<|t0n z&f?T_6{nuNIAxwNGqUn=2}90@Kylj}oO)W38>lFHdU483#VIovr_53oP`QLH-0rY4 zvWjsvgC`yxz#RbQQ=r;)M%@IKXi(GkKC2!pYZ>ISOf%#%0<_0TpNqp5I%ojerOE=I z83d2^cQb*Sv;53+nK%@{1I5yy>h(N$Vg}Ui;81`xJU~PG%u85pK+|y?%q>jd78~<5 zmIxLzRsrU#WxH5;IfPjpLF2P^PgwPt_p^KgZwz8VanE%o(1`$0_x!4*nS1Uax#xc^ zZudNd6#*xipFj%>$clCFlmmRlI=tHpU9HV(!!aM!(bZ!WNn>SYI|b>yfPAU|D_s77 zvpYW*b1M^Q99o{as~*&Z;%ENG09w?Cc3eNGmB-Dzs}^)UI@XH_K!Xm&HdiU~9br^!4WytEc{v_B}KC7GXBdV%(vgIcG|7ivM5 zM}rc{HCBB#aJK_A@(7wtfC+-lC2FXw)UxP~mX6YxOs~(t_vVyi>d$E{-<{7-e!{({;Sa~M08t#USdxN&} z$g(ki0Zs6+n6U~o_tbN&VYNtSm0@Et6k@ex>tg}!LjTHef<>LVyPgBo1P3pmo4{hm z%E~;U{s|j%f6X;e_WA&tGXO1IQ)gvi{=f_!ut9MgXmLFUR}9o?p!Enar?IknL#k*L z^)px)St3DK3jV194SBFKw=qNS@j_Ad8eCdrU@_nmSSA;X%v8vcMH}jEm@hJvK}F1u}x}hf;55m4z+rxhw`*aXb&)vjB~RXtIhhPmlnOsaY~N=R~k%v&u6+ zteL>7&RoSY53CWq@=msdC7ach8GMxwXq=6Qjd=lc6X==<9yS|RwKSG&R$(^gn>Ajn z9O10O-fYZ2YrQ~2tZd9Xn7mlbn16u1z`7PZn!8Mp!v>Kf;fZT2E2u6L#Uith6*|*C zsagTDY6HAjgBjFm2Bn3=m7u&P$2_yT1kzVT3C?S*u!_Bkh>9JQjaZp)S3bc~THj%V zy5mcw0&-mqa>GVeP>Vr~`E3<8H+%uNBH?>=Pz&7_c2LCv-$;up*~1PB73lB_ioYg< zwUx5SgLWGUv6wO6WnnU7WM!R*$eRUoA)(7Wj~(jrO`uJe;Ds-rSa9_1VVif*kJHE3 zw+Ag+-Biy3n(DxF@((DScCoEt0eA85SAeD(a*}tF%3&T+6-2} zT>6BSV<%|s2CF*T308hKZ#L#r6%njv%-h&O1^IPQGGuN9%`JdtQ~pE3Xfk9+1&W1- zz~L#($_m+@f*K7cz>>DuB`@HNqbux;th`*ppfZL7)Nw?y@D9XGEJ?P515(bqXMl9F zV(47Y!3dft;$ps5ehoBJ1gZ%6nWum*&SPUfR}S6-y&1HKmaT-%a1X2aEH>sd<&eD| z98FkC9MH}jK`!QC(C!Ry(E?g70LteaY|IxqZCF{;!5O!eHv&uU{Kv@%y8CrOJvQA9 zTyWjjc(Cc7$OTH@nONd?CKuS;)_Tx#La?h?%s^{Ni$Dttzq@}OAmDqB%s{2_d`^%NV8^idgEpHLfLA1<-v726`TjSM>)4o|M}sbi5MjPr z2U>Osx(NazzM2uV{E&xvEf2IhUYQB9fRGzEfG?z!HYs)?KWhz)ggMwbt*`io}iV<2Nbku`ZGJ?u&7FbX38hlJzAJnoq z!x#ZN-vE5oAc`4xz;oX`T+E#;j9`m@RySes=UcEc4ld^Rj1Xm$>!5vN6w?}DjnPKt z5*9U9(8VxbMBE|8#{3N250GSIUd#+SlZJyC&)r$bH%Y;le@EDYU4O3z(s4dl2fCXJ zrF96u<^{#Y(_k)s%>;5Wr1`AFD!_aX@2yub7juJ_Zh>Y|z>R5qF8cy?*$zyXeXoUH zK!f74Rp2fI2N&}uM$jNX(wUH;b#!`c%txyAKv$9NV*zdOumLTVlY*?&K{^qVjd^)B zXiGfv6-I2QKu!QHGdlqaIX33COprBTSHb7rL6(3)Cb>|YHIo@MMq>DWyg+v+f}C?*7j!WQXxPdaWG?f8&^4fOaXsca%zDgEYQQw4Ta04t0@y6rW-SHq z*0Qljg9$B%`Q-curW_ziC|-XQysxPg+&k2#6pRQqcGh!YP>)i zm{&1Fr_)fBU1xz7KEJCugz!v>L6>2tvFNjMFyE?p!pgc9vitns zumfph1hpSfv@L}-iPu&WRbPNg)>W`3F^WlhSwUf@z{R|$8sh6?EF2|RLT3gWsG1jr z4BDcYu#+8>F?hI`*FYv?_?hR_gKk48VU=WKURVdZScK?e4P})bXm<F!jhZsvoo?vaoMu6LMyj$I3J5Ws7u{f_k=}&Rh0QJBk1IS2v#2Esnt!ODR%)@4(2B;p!sqJHs<~+ zre3gq3D6F0&`LB0Hs;w?Oj|+wec71XS(;cy+2*k_F#oJ(0DLu`+tIG0&=l?yK0yXv4M((!@mx zq%QCZ3g+v~9BI&V+nQKq*+9pcfZCjFO<=+Gl^l9(pm78aMewQ*_%UvfRUP0{nnFOH zWMSS`kA94s7ps^)D?i6hRu1OJ?4VQP)R`BReFB}n#{7cQizS>@oOwnWXbqA!xFhtr z7F6|tmz9FHvg2qKKxe8WSiG6nGl7pEyT%s563(i^yrc}WMe$SZ1XdO1J|?JFo^gWC zsAgc^Tgm}CBaIt#cNwT(1zJM|>O-AkIKc+0i9zRiU4tIW2Z>y;HwBorydWo>vZXC~U(vLLJjwY+Z%E5dAbQ78y^D6K` ze9Q+~CNOhB9K5#_v@YC{6=zZerDq0E3ug^zMciJd36Ku^;ab=-p4$wdlOdOaR!e}h zASjPRl9@NCpu5A=#Kyd&wuJc((+TD!wGk}g;0>5;%!g}BnD;W-FdxR7^lm_sUMDlh zH5LKp)_M+`I96e1Gtg)hG*4Yb6c5n!r%$+e@M1wb<&jlPpN;uP^(W9$-*y(zj^)Sf zpkhIa`9m3Gky=S_2su-O6-=m6iEy?I%cOjiTfPGb0N>XvFVc4XBRbVZP3)#}dKZ4z3^A zm_esWf=|Od%D|xj-b$eYIx=h>vmW@6#}bwZRw?EK40?AXN)L^Gg=c zx;GByFRY+}V=fK_un(@6b4-9#Y$z^y4s*$Vs7s!LUGlRER8x`YlCNNw{H%hzImI#XO|~l$0tyH zvoSAZv4I>9fa2g&EFjxBxS0PlLfX80LA%#VSvi;=v2&C#b3I|@XP#Wau?AGzFdt>` z0u6wHFY-h&9e~QiyuNB4dh}ewn}V75L*r&WILWbq8b_yC z^;l$?C!?gAe-#|i1yLw2X#+QVp_6ndGQD6KZ^!|6AFFn;@PmTXxP%3Ca_cTumLOIy zHc(XxJFN8yasy}$p8SZ$euY|A5}1rWOkqmboMU37z8yaotSS| zf!nUTDuj(@=LY0ZI9K(Aq7<_kYY=Qfw&GM3sE89_Vwr!#qM}iaCVUio?iO?1p)S{1kHxt1ZV_p{3O)21tsTP>(IeoP{`sRe}vvSMabg zgBDTnvvOcbHfaR1B;I7xRgW#%JOJB%l9lx{mc;xK?t?mr4<<4~2Lw?{k~TJ|M;}0y zEyk*BD@@rIsIv1|m7RnsTMSk90IRZlFl7^=%06LL_8+F~b1lRR6PU1hVGBFR3&LE? z?`k+(>$c96E)Q%KY88u*$M*m`|%vl86p!Pc{j0Cye!vx>5T78R$1%Z~NT zHf+o%D?rUB@G;HUIyDEt^EA-W5|r?M2X-t67xScQQ0teK`2!1dx)()RI|s;599+yl zt02lcS+Oab1v{A!I0PIbgoAd2J}uufYRP>W<|HKcGlfz>JRkyK8Ds(g)A zWj`m#(ZXEJTPk<4N?m6aX9KNIVqs(MsRIpn;92Gi>V09qS{tSN!Nv^gn6Q9W!-DoT z;4@GgbW9?+aRq9eY^ejUTERH*8)P7;H?P3Pe4Gh%`jH~&C;-r^=vvS)11lTzBu-Gb zl!1-;UD*lHq88AeAPxl<0g$On>p?4K=JYpfh$U?FfTN}hQZTN*1zD62BZ6jly5=56&H zphlA@^G3VEU zC7=bn1bdO7(E#RC43I@*pg|AFu)(JR(+}zn?c(^v#(cjF=~Q%3caXz|c_X6@h_wsO zfDA+|?-i2qc>=H6FfbN61NAyDV~Fid2uCZ zc|0rgeKzQ2Feu8NfRu4D&n^QU(ffi4)c9kb0qUBDgZjPCq38Xvpy+Af0%ax+F6M?x zND0uyJ`YPVKMSVpSp`Jd9IVRr!IT|>Dm#Ex*-h{rI(#OEYJPma6tt{$jr4l$TurV*<#?}!!1okZt7xSr#Pi)Met2rWA zCBS3;;Q7hvOt7IN0aiigxuu}NAs*)939Mqx;0(F47POcFF`@!G91JvG#1D!I$mI*G zYC**kLJ4?@7U8YSq&QHV`XDL%B{!7yc@JVQ-IY9 z>2d+Qbf-eg4A* z@(pCe3zjj0``np6BYKV|@*2s*!nV+{+WBmt5y(6ON?QIT0CJTrE2|Hd{MaYR$jZ$Hy4}HxRYs4Mm93f8 zKAe?xK9*!ZNe~*0$MQLLv1qfhFh66oVPig6tH+YVX2PP)ypzcb%mocKz7*7BW3m!r zW8TLE+EpIT{I({7mCuirhhsh{0-xuB7hS9r1YKCc!+ffULxEND8FWuhII9%%VF5i* zTSkwK1GJnElyD2Y*wP|c!oh>KDxm%9UznMUL5Iygt^w73U@hPzH@!d)q*59*e5FZf ztXLD|*dh)+BK*TD%)FzP$%T=XBOP4c{3!)bv_Ar!Ccr$8)r0=J|r4NeB)$=F?T6K?7apt*oHEyASi4SozqPmk36%@^C1!ialjj zX9EvMvN3lTc(Kg`?GqAZV+LI{gtR9J7DNne%+K<`i%~(14A3|_o<;`xr6S-hIT&pW z7Vz*6e4rFG8?cD~6UZO;^T3A=@~|;~;RZR2fsMHbl)4>3V{V9{PbOQ8;&hE5C~-oo zD<1|1N?bV^LzBRTsg02U@E?t1V&i zVdZ09%*=6um1iwn7Bq@&#{7*NROWIsLrywPV_^mjW30;pN5Q{3FBTsVznA42xDXQp ztr28nUci_J8TSBnC&1$#&lx~h*2t~Fd1DRevYaj4hiZKhMu`yq-1FgVeUd8wcGDdQ(E`o)bm4kT|qYcC@k3n~WM}s$|B_rKztjLcIVd z4Lmm5!~(U)fR%ecDBMBCF6d$lXi11`6&z^HkBFu(C>4Q*$DzaRKCB|llbAlSax?RQ zFMj|ZjK;?NgjJ7KfSCt0qi(~-JO{jr_$O}@s}OT%p&lrPn7>v*F6o%h4bsBF{Hkgf zix2bn8ZTB>CM{v`Io2<$LC2u*FrOCVn8(JvI&%W(>bF3ZSw9 z)Szr)y9PQ1lEnu!A`cq-<7Z%GWC0~ghegmt2^taO=Hl25UT1!;4isJ7%$q>XU^W}( zk5%A9*C()o$K083v4KKThItw2s#VYyy0dj40dD545cQx{D{RaU>-2CgTLGmU9yaD# zETFZTI1V5(WMiJf3Yz~yJ*E(p{oy<7KnvW#GrFME#eA}40*ey!q*_q!@nU2CU-OBD zpN;uB=tR{BX1QyuEJ2`F3aAmm!N&ZGdmc*!D+BYAQX3W@Hs()tpy^c(NIq9%6=J?! z#qkL|L+pihhB%LvgZWz7E;i=XwI@I$uxy|KQSf{sB42nx@`VjjzJSanTmy$M4;%AT zR+~sx)(UW(ey9SKWva|~nK|Y`w$MSUTTnDXB8h>4c{VE}D2;G2x72WePF)jW-Y!uB zI+c!N4fA!$2eM^H{+rFfs23JCB?BAS61OA3>sjJ~aAIL!*CdB?o9983*$|aP%{w%%`)ju_!S&gCh%6h;cCA5={fE z1v`o33CKSzKCBGPE7L&dr9z?xRQ!U@Q_%z+u55!)z6MhCbuoh01Bx)8l{f+Q=o;|q zW_$qvO0CRuvOq^^3NU}iyJ88vd%3e7RDR-JhL8a&SeRc{gHktqb=3r07EM+T=54|y z5NEUGu_`mSWpW6C9Wad%ba)%J9Duxt3e>c&zXn>MIExijpa?Nz0t(u=0M1pQHQgM{_sT#UEIw8umPdg6affFY*dL&s%lyo%v-LoGW+$-Zff^V0 zK?iBEiZK5W`vi>!jtIi>zzn)YEPVnS^S3%L$Y}XThEJ?K?ko|^;&?KzAowI^aPDQs zoA<7PD)ei0(DVn|mkY{zptkUeYET;&w$csc2?pk8Ri9WKS@oG`u{MFSCmZvJs!vSP zpmMVid3^`ycv4Z&S%Z8otn8o}PY&?T5jN(nOljca?}Z3hfO$qXsETi5WdLt21}~(a z#R3|0!BPBySH(fkUt={yL@20iy2Sd4)quH^4YE!5Pzh+cpD{CHQ!r>-rzZ0qmNXX7 zt?5fZ$F6|9`L_tZfIrKm=Zos^m9drX92lJs4(1KJ{1JzjhnJ00BssT{rRFjSQ zR3#+yUuFYOHT^B}Vku-5VPn3+1k%jG{D@@&D7-(ZsHPBsOOv!V!ExZZkY%U|S#rav*5S zHYnFfvN4|)039HKnHvgO`I+y4W{FwYn%J6I8P|ey{}*sC5H+kpNB)4%IX^1+X&uD$ z`QY2~z=y~+LCSlS}h-mGI!BN+G3U2}>cX4s(Ad z=(Z@($tE0Z%q^f3mJ3-8nEyZqXP8gdrLln;(x7oB=&~T_(WBteR>Tc}p!t6e1(rfK z<`+z9pk6<;S^tF_6zSkL@d?n1p_fcHY|PVZk%oStt9?-X^p6R2pyzy6AvPOU4lh<# zHt2u@ilQcFMpiAZ`K;3T6!n3(Ae{uQb;M!JBxXidX)ZJsDxiZKmq6-NW?N8C5jsZ* zl7I*^FfjjRgl?0#47vad+WrIW?S%FdnpjymyutIozSkgYnozWZHmnM=@^dk7t%Kb3 zr~)b(Uoq;jvN9(_7XzYbSPNe2V9Uyy4(YR^NJ7p!7GY)GfJO2hwJnlPx2w99s#iIUAFcD60Y+Xw<2f)so{It1{aJRynphQ0dZK13JSJ z_fP{kpMaNxfeyHs!wkAO_e6~sE2jr53&$i@&M;OMCRI^Z&QOqH+d-E_fx@JXMURd7 zSM@H?pidL*$RCjRJ}iZ#4(2YF~6(kcmnF1Ph{Z`VliQrV*bShYW{#i zN0g1ZsrD1A>~2tT$LinM3rz(Z$2Ye4v&?k5!rZAm|Vr9_IbEpV*ksf(~S7 zWn-StvWt!RVYMEJK_tylLZrf7gw#P6?1G{M?yH3mU$HUYsRn0)6)bwJ$~vs9H?XK( z2~&FoqILtS+B^nMri2v`lbPGVXV7_rR(FC2mqC&ZFv)H#k_%vx6JU}jAtOs5od;l& zQ(=>|Z(rhKH92}sPSLu+AI?TvTkBbb9EdE@~>*}w8CZn05s~12m zC46(=NUIl^L5cke19bHQirH5oX0NITx5SVpp-&K)BSkm<23F(mLyTVlbvktRnL~&~ z!yjQa{4vDvIZ(GF=AA#0X#6v*#=n6WKbbs1F1lpz{Gqz)ugF#0wf?P+`7S3?65jQO_}t zm52ES1GvCrW6p$K0>%u|1-kSBJe2i<0d!m$2lM)R8y08KZdJx7;Iqiu>p8Ar-XsQF zFKG=m1GFZ29TUij9Lzm+pmV@&ScRF73W6?lY z9H1q|kS&g&@DpSH#K55d2`=P2$Lv@cz;}>AuYTk3g53RP$HuG<9q31itTjxa297uQ zdZ`txpitpqZozr2CE}Ja)O#&KMI6<6rweSDz}uZrtUL?0k~x7R4b4{Y z=|Rl*>p-WS@gQBM$pO1R3GFsbP?&NkfDbnZTMoNX3Y0cEm|qvnV^L?`#ld02;>{|; ze6qZWjd>mDcrox;#TL-z2jXlItlSZ-93TxmY|PK<^jLgY)tTooHbF)>Pu0!?6^(4L zvM~ZQ!U^gx!j2SV2F(gRVE~2U~wZuDk8$^%}4 zH@6rxK*7z%{7D?N;|M$)!^Zq312h66#QZ}Pe99b)BOCLYJkSBQpzcozxEuK$bzlNC z5yi%QmJxI|7eDjnx_RJ1gpYN&cZv}+|RJ8AJeOr)O0hpi`6x4dNl2$j1Dx zN{Y8R4I8Lq4Z7%oB?vnBVh&7I#i$ z6=5!zz^cgR1?txpgN{BhV`Ki#2O6h?4bguBpIGe%8ifZXYi{N(jG(nJh?VEpU@Om0 zu!=>pF|Pmxvp1+;eUk~agV~D>beI?i^LZAI67X#kz13c@+a|y>H19C3umxSSgLz>q zxSVDFkw<9gTM2EE1~~8$6DY7E)`U43Y3SP<)W8MZ!393UcvUU!xRBtj?T=+e1R!~ zjd?@u6G$r!rKr0C>61aacPKKqA%$QN2k6#r4rWSFu*GF_A*i@m%k%^sql;?SkRPLn z`&*!y26mMmBt}<({0dsO!pg$@0;^wNK>X@TxnDsSCFvn5Z`9=5R%pYb&OD!E9<=WE zg6zEkjii||ujd3ETLLQ*(-1|X7Yk@J&K5?{@Did(gm1=q!YT&2lHnp~*9PstFF}%PdTqjNs$}wRj$C*$9hf zP~pJJycSwCz7pn8fb7Rv4PMOwXonA_1BP;;Y4{}2c-<8 zW*y`v+I9ROw{tLe7TU0AGQX*v$KuVrl+}ifxv2_V7lB7JL5(90<|2+y5J^^{bXI|< z;MwBOwV)$R%fQPeKs&nRnAfpF=8M-J6H)?*ZMk&Ag%324Wr?a~#qYn&7>upg9F_ zuL`^$4&JN!&uzm3k%t{g18UWPR%G&k?j{BAV`N@be+?#!R%`Pxce8NVusAbs0_|`^ zTm|n8a@1^Q@Il?opj)Cq_45WM(A6J2%pZlHK+@nstZ5LmAdC%k<9%fbY&trEZ5Mb+ z3o~ds`W@C{>Jt+qi!-Y@7xMv7C&>qNnQWa6t2mn%=*(l~2v%+{HfD9uS_4p`f;9a> z?SIf(3DDdw<2=xOAoGTL@Jfhhb!$NV0#KuU0<2@ep@7meXae;N_(6g7xh@T~x4Vzg z2EDysx&YeV2RG8u+WY>@(?IP$(0Ma9EP0^W)H~qWQ!iE?Hhor6wh62}dZ5$w*_an` zMSwa!yI6c!!3|9>Rvkn;(+lJTJ^quh)Tk zDa@-FIY1>g5A#~lCoFl)ZQ0i#yN6K9k8iLjv*7?=4#WJm)`kUiq!h*sHDtDVHj^GS zLP4t7nD>a6Kqm{qmzgl*owEg{h$9&jpa!r)Q-maDiU7^_fl~x{hM0LfBt_h>n+Hn~ z@U9}!DFV8zW*bvUDI4?2+BIyHaUNu9_+6bBOCBo^^CU)4z;Q5t5Q9vfgA$ZH^W+T3_V+Wa;GQHXkwDWK_yjXV zI*VXl3|{&JPF)l5r>^t~;Ew)P2GF^}3~bEppe3(Zb}G>)kwLs~3_2d2`IiWX9+ts} zCT3{CU;xVN+{}&j@L~aJ7ywijfMQ-9l;=Uy^$ei?29_9RWza{<=4n{6Ib>Z9s9W#D zJd+WeM~~NniWpWl=2m9VRUG`x2Wla^?aZH0nmQ{-Re{mX%5A#Lxbk1uWRQNDCl| zDT_vlX&*E(O{wEJ0qfR62ldDs^nnBdN{(3pN=$bliD@x%VtTL|QZO(thIkURcF>xY zgPHQgggP>Cun08pDarhf?Fp;ed{#a-CM6+O9yX427Fkva=Faja@NoGr4p2{yg?VQw zXf+VNo(_Dn23pa8x+;XnCqwkh640fptC^Wt@fNDHZ z<`ht0iIsUZGibVlfsOeVXn#IzloU2-0N!i~I>Q6hMcc&!9yFcL-NeTHuq1+onT>fF zsB?6y8fmcf!a8WeS;@l4;sTmnd{T1_TweU;v_UB?P9T*Q+{_OcK+7b&n5R_eu`#a! zuPxM;Lh2d{8uWB$$zYCcLc zH`ah};*(;24BEKL!90hJ1GKmfbc7F+3L_|mL@;k;1g%m=8g;-PTWo2dvz0)`aXHs*zupuup^J)59uQ$FT>g-xtH z*V&lAmxHcUV(#LAq@CrJYuMI+at_Efg!X}eqxuwRnBE8EktP-i=8cTRYW4x0@jH{9 z1Kh!Gr~t1G1*PEeCQ~FL>Dgr8vht z@aTRLD+lxD%xjPm8SK1`j2xekM+?@loWL@4wFZ{*9@cQAVNH7TP?BCNBWTFSi}`&y zIO#z)aA`-dF|VxJ#iGQfmO0qWRYX9NDvKub2?)@;nlpn)*>eW?>zh0<90n?NZUWaDiKNN*C9SU@M2rh$wVVZJWe#Jn<% z!wbrI!s5d!&BnZtg9EhuPl|baZ3!zl{WgJXVr~vlhnSUlLj`DhN`!fZ=o4t~o8uZN zhhgb{gDy7%_2+*|fYKm0^R+zi$$HF}%k@Ch;_EmdYjF24gF?iJ`A7};c!f*l5v)ec z>o_8qFO`F;U+_9^FVH$}@I8cxsZdY{7F+=C%Ur{j#(ci+8d7rfLfrrYI-Uhl4zjIb zo>~71vaEbPM+7T3sJ8^_LUXe*ALIuerRc@nm!Svp9~<*M2GFn-Vy+dm!hBU`8mkZ+ z^CpfbtOAj&Qq7g6Xv+Nr)H^Ss-T@U7)7U}jM~wMb-8{7N;VfvE%cmzGQ8wnynV_u;$Xj2) zO}F(dOqPsTiiY*DqTyc61gs@P2~x>`w+XA~4Q_sR)~2y?Fi!&213s)$%#UlHfb$c$ z84Bv0Z>|9C^%P_7XFdUPFqSqZ^e}ytZpt>Cy|e~aXy<)LbrTgj@6eH7Y~Au~Y~6AW zSVta5uiOf~R}QK`K)rI53rxsgM6;C%)ZP+dK2y63+AEg^C7`p693>R5oms|;=!}Eb z1yQpzezX`{XM8RAI1p@|anRu}GF;5X98KUZ90ksScE(Y=aF>c9UAU{P;ET1Hzc7G` ze;(%M(k4*5`ClFU#<4Zv&Qt_i|09ismzC&AXXvIv@EElmXwe;H^bkDR54xt7pZQn` zhXUGWDcHy}XtNZk$Yx%}2yPfNU*#%6a}ubT3vbjy1_JhgkIWQf?qUKRCa}K(v@950 zE^KFo-ZO@h-L`{cp1A)W<}OA^Ren}t4XCnWZcqP&qb^45-va4m-VE6`lLo5syBQotyV(Z%Eo++i$f3MT2L<@ zG_=aW2Hr*rDmkulfkwiZx0fMOB}x*2^lSD*lR!Hx2`Do{diA@oCV^d$B#^s{()@;X z%Krv51)OIE`-ORa8F=QN`6`zexc@h!w28%%6+SX?2+Pu?qr3`1OG9UX?~ zsgI!lFvJNK=rF`y(4Cy54nrJfjbLNmTZJ?X0U3l~U|>GR3LS0)wrgU@l@K-pa8E_V7|(A0z43K78(>^u?EE%SWq0SRzPl?g9Z`6 z#nE zyWsnH*_iLu&SPVKR0W!peYg_;oBgOWW2>%>iuJ71daG|F;A@FmQbBD}Nce!qeLz7_!otivwR#?_BHIa89wt2@(291@ zc7Mof^)2NQEH4w%Pk$aalYhRGPJ z`2{yF%^;7lFoUnpU5!}a(nQ?&EjY<=voY@l1uCeabDQ-E*mujyKsi91`8#t7XhSt~ zL$w~OG@BPI2a~oCsBOo_{Fuvzg_o6wxxLH_;&4`eHc;qsvoQzlVr69JgCvBrtZTrD zWmfqvaAE=F9}edCoFISlGjFJxz{Yp;m)l} zaE3r6VDR<^9yaFrY&LAn4?#*z*_aoBl-{cZEk$5n$d<;&e7_RZoo8WVz6w&dx-t#K zU|wAr0l8KOrAXX`6th>VK_}uvdK*|{HVqoHhS11)g)?#>rN=xb@IrNF%s>S-mqCH5 zha*rqKz$M!<^V4ek^?gD4$=0v}l)s0{B+t6VR|Bwpak&Ll24tNG2*l-Zb_fauW|^ zMKg-bJVr)VMlJ)$h9DG~z2NO=&|WKw%pI^E5iEMXBJ1%+cEM)IZdF!UHqdHN4mRe` zETDCttjzt@&}9uM%AYfVj!0zY=e z8FT?S2lGUhH7wq&jLZkCy;wz=RY7BcJj@q_L33)N%o}p9fyeog`Hrlr%r_W7@}Se@ zYe2Jn;7#y)pdB8dRs5oC%>B%eyDJveML=&Z@CI!S?gyRj`LPCk64gv*8%QF@nr4`k z8R3cM1ZbNOxJ7rSb^>@+9NL-SFmH-5XQn}ehPfSlj}Jc^a}%o#cpDDtX%5s*P;01@ zp4PB(uz}K(9%xPLEYNw%%wNG*LV>m$SAlkP@<6skv9d7VVgMyKQRXJ5C&-Bc6wYEz ztdgPN2H!mn&`h8x^U`uJHs+5tXfaa46Bbhy!%^GvpG#Z5+E;G?^PY zAX6dWBV+VfltIp($_m;_=nWn?X{lMmqRD(v9CFj87kE$`vT6F304VLTGM_F-vT4FL zXj9`P7xdVL{xS|d7H{THpqb3ArJ(%80$QjHp8EsMIe^bnyU75$CE^pO4P?DC8}sJU zCeWf~Xs+e3gJ)Yg=3ktUZ2O>$*c02ptCgQ|g0c`db2|@cSRFo7pvk-wyjb}YC+LWK zW#*NY$cvTJP;)WZa#mG+Hs*Du9H0xLn6Gk0u(H~Mic;p^rJy5F@J+OPL5Gf^S3zPw zt(67!BsFhVY3B2w9ekh_aP2%CpkQGD9ash0c7(oKor8_}3Re>w^O{nm2FD3VO@)#U zh=?8VQ9DJTW#pis31_}s1ziRB2$ue#hn|6s3WuCk2D(@wg4K(;ml1TJ3GC$ePvD{g z+#OoV4d2{+0<=hhjrlCs2~a#UFRMg4%M7$C966RJ;OIVp5))z$3mVIyxeZV(qs#{( z#WEZ7R&d(VVPn3|;KeG(Jck)_wizfjQ#XP^+aj0|D=CoHjDrrsgC24QjbKpq#>3ps z!vWd^2999RSyn_vF!Y2FPy~P2L45W@jbMLJj~0|ap9_GKCkml5#(oc|xegY|kSzMS8mw|hFU?reF z6UIyis5%k=*&)We#CTYlj6jx2Ca05S3d&r*VKU zjewpm$A)~o9JrBjlmRsDj%(NmZGr=|OG*IbKvrQk&~!XE8*@7kQAGgAv!FEq_46QC zoP1_30UvdUR2-yJr@Y5q9DwpXdT}7jD#rY|20XpZ!#stbNu7}ee9`Y}@Nq|^77P=1 zLsAd(KQ2aANiJrQU67??93V%Ly>bk#LIatFy}U#5Ab8~%s~B??2c&TdTPQ~KKoMv( z2<6aj@Q&4)^&AS&1!AnMRgl1XSb}t(IdoGObX5#wC<(kZo`sEhJ~wm(9aM@!f{KBG zxt*JlRSa}AnaD{t=5Hk>teR}hJ=~DxOH0cpfQDYyfLsmAYM|jQv_m$bYb>C5jG?(2 z)MJxiUdr`}m3I*v^M{gMAbH3T!8I0d&~*N7PUw{^AU754g}7=kxyfPQ;ByniMM!Z6 z8Vg|l#sJy@qRzag47@RbxtkT-_c>Jo>U;XIG4JMpoZEK40z8Y${FDo{k%EJHeOVfd zKXV7`1eQGJ?rO;9uL*?C`6u?oTu_aRw6P3wC>H2wMsHS4=3Y+ldA`g`%RYgUSrc+H z!#j!!N=8VT1D?#-n7@>8fDQ#`p1`dK$sHd`KrL3(q6CpU5Q$J3G*WDUJOTk41L5Ja z19#|Vh(xd$u*x#O%=Y4V%*qR#i3|R#rCX0aqw$moPH2igJmt3hidqjb!CuehIotn0Z#+JXT#9P(a?O1ML%I z-p%O6>>j}?3$DZef(}~ZVFL-WF(0jq09_cciV<|`*dA66<_<;$R#xU8uuF1KY})~L znFuQ@HfNuPN;0r9Z>j^im-!+i^tNIYL!K~#I$Of<qP<_yVSKqY%X57eFeRxj3K~2A~+*zziCf!r}A@ z;A`Fx*TwC8Iek4dG$daMLYzK54~NrFz$~=I63Tb*NrFyl;{e@C zk4q#s+m%$`Cys^k003Y2Y z!pgwLytn!j=m4uzEZBnX99Uf$8}s_=2vEJ+#5|8xmiZZ@7b_q0&boQvbOQ-|4d!$C zpjCVk%rmMvyg+J~FwJ9?X5PjGQv0$Nq85_I)R+%I)Uq-#FRSJN9Ujkog@wtP5ps3} ziYM>0fD#Dvk=ha@!y>>IqZ&4kMIJN>1!^N1FfU?aGGkSy>I(n15F(Ft@VmVTqIuh+5EMZ055X zpb-kleaoQSwl16_jg{Ykl_Q0f-;kA~o|V;zjrmiR0&^c$qd=299H2ynHTJi&flg~= zuC!rgW#;679$tkK>iZ!o3PCCaIiMHvpr|+wo@^*(<;h^>)nOH6+r`SjyuA`!{+nu`%fgfs&T&1Xd2_@Ql#(Fx#@Z%UIkpH^8IB1o2B3L^1v^Ra*fG>8r!2{Hg>GtlO82~gW{1K4Z<=IxxIaaB1EDHbJ= z{6x?yDmBm|)yD!H;D*WE{CS|!H_%u%sI{rdJdM>0OmXq!ACcP zN5{6q4+!1`Iw*D?IJ|#@iVIMH8?kb*acHs_u(B{;WG!K3l2VV=8IrH5A!P4d91AdtP;#WtG&PykLqmLnBzg3S=pG6v3j9OfetA$VC84N z%m&%Ld8ZO|{U&JhrVgtB^EqKq8k1taT^PYC243UdS;?^mWGoxAFq1kXt3Got6UPJ= zC02gs1i*0y4~9puJBVY#b%vi$56H zm>VlO^dN(bC_&oF&Iocb7jthVM+x&=Mo?)AT0nP#F@jYbH2=1hX&xJMa~0TQ5^T&( zOl!b%x5vQp^FZ~;574%GNT`Ey3Af#OghMP=0{ab`i!jN%=4I-+(1>l z2=g%(CKK?%v&<(eL6?0%4xTk&6=UAZ3fUMxv5G^F#eh|q`8{h18}s)Hj$JGAui_jdM{Qp<}(~1%Q=|WRd}(oPK4B63doJ_+2D4k5A%L@$Vuya zd6;4uSrk|~m=6^*X@G{?d6*kiBUltbWy+Eu(DARVY|IN8K^L@tV+5BgFKfW<)rE`^ zSj^u7HGdh3`Ji#Ld#SZl+o#oP}*j5dvxfq8N%IP|)hKwkgOpuoz<{D#Sf zm3Ka?DBBZO2IfgsQ27W}QRZjN;P9G&5EEj4%B;XD#-_(AWW&n9Jh2Mg_~`-_rQd25 zn7f#u4FeRH&0=C?6$dpg(;+DcMG|!2F;2+?;GTg5s}`FLtELyLhz+Z~9;=-Vh!Ld6 zDgvI3V`IK7q{jm42W`y7W;^JhX$~$CR#ts1woipMim^FuB|gbxU`gg(^^B~%%qJN1 zScUem%3Nh*-d1xB6i4Tn_1Ks<)hIBZW5(vF2N2y0z`7qX*uZ;fsA0nb>M)9e=OKC7 zm}k^;l&}gt1N9skuCekmPpvOumEo{u%5r1GL$fP*MkqpKT{35{dx-v=%o5H z)u8+7wy-p@F<)h9VqRT;jSXxAD;x8E7Hpoo0B%u6f-X}6cdtgrFQEWWy@TmH|GsoPl`)B>8`4wP6*K!N?V#LFXFvRw*!lhP81~Y-okFQGeIi zu*x$3`?UC(zw$@0F|W!5<#P@;<|k|stgI8kqeN$_))2SM3alFW zmP7?sA?9DK;Qe}F^Fgf{(B|Xg*`S370?fZSAY)}aOF_qLf?C3?a*^P}!uD}-fc7m2 zFkhLOT7K<)AMj2xh|ydVSVY|M>y9H6PG zFgE7b>`iRUQ!9|>L_tgCFou3nV&x?`%Alv(qsV-R$bhcy#*FEIkR-##{J#QxhIR|P z9;+Pa%%BQTcZxd#OA75^2lY)v*qFaUwDiHWzY|dKZ=kST`rgO6EXh{m9KPR_0C79N3yzOTo9VGB0LgB%(zn zz}(1$tu3{WlaW<{i+Nr>=>E>f40_lT_*aMu2kVt!F~ja7lIgq4TQo0Wrk zI-?gGb7vVi_5bG7V`FYDQ(*oLyZsX-yw-7nZCqE!2d=90jzsA=4KOuQKN)H3jGlgvt3QVJ_8GY^Ls6ElXM<6u3AfozmW1YKXLz{UKp z1{8^2tO{(UtUPR9Z2hbX2CO{phOE5IP0S^%3e5j&^jLYB8<|0KhW~5yiop_~O0}UD zay~O8pfs3Il!4D|npZM`Rf~=JIMXLq5%A3y%yT#+Sc90Kl%=uSfts{wNNw0Oa5w09 znI0?aG&bg$C0@+WxxJWJFnsa}>=VQdAt*li37PG4V3lD~7iQ&QzE{Ix;~g0p1&(=$ z>p;=m()Tq4o=Xv19-j`(RKh&G;z!W?HLCR6OcNg3Cac>%wO3-H%uEbzhP$b z0(D8bnR~cEeP=J&)@)5y&>0M$SQJ=AnE#coVHINLfizQ^xIj5eiup_F8dh1hG**Q) zR^>ESwKP`E2v(hWtooqSKjwpda-xoD6CFAWFVKd;Wjr>l5^SKTNk@(vR%zzSCVUb$tnzGFb&x6{!yNa7Ri2H*i}_D6 zD4dwPdAz)vL5E0JO#la3cRf=au^~1g0=!KhJefWn&7A*rWSFDG#taQl&{9B9OLj7M z6aKUaX~!~eWP~~f-2Np+7YjJjK_x01^K~)MW>)O&Q_v75sC~);o-qWC)IuAg_y2z<ylV+506_tcHDp1B z_U0;3fP-=m4Fds@PCzSzv8EGhg*V75XfS|Aa^b;%oWgWbQW%q}5U5+G3*TnMl*h=T z0P0&#U~xIm7=cN;ZT70lu23S69Ha(_eJ~@)H4dKqRelL zK;2Ra=3Avt&@vjVuM8Q%T?Rf+ON5PiCN~wk%+Tx)N(T~b%=17utx%NEarDelZ!<7q z5suOIGTE|z$uoSi}{TLXr==)*9sa2ofywi!p1zgSb_O154H~FM;=fYO@viE5YRnBZW0r!A`Cp{K+{mlP45hIc)6Waq&xg(T z(|BR-WaVWp2hF5%r(tQ?FF;lXUaBFAP1!22GUiRyp!Pjz$)F9Z0^1~39uHQ9DXcsp ztP0aud6=sp{h_@)OaY*7FAwu!Xb)#E52#@W;&VWn%6oY@gus2I!_Yp{UY-e1z85P; zKPyips{-h_XB1o9&~0%A*#hb}F{v=J__G=_i!^~R>wHoH>PRuLF|VnM05#<{>VejL z88h#X@1oK*6JuIE| z4ZKLVlcV$_w(>HvN`WTevGq1j@`4g9^Qt=V_yzMaPEZNP%Dfrt0OWmMuqrO*UF;x7 z$ud8w0F73rlwe5fv&P7 zhGb>i4e@^|^6VIBF_{n-b8r0`R&Hh$FIFMu7%x^1<_a%n9UC_0z4f4LJeiL%l(6#b zX60uVYGUPKYhqqruLnvZZ$Jm>F|V%2RC^Mn7SvBFL8v{!D&ftl%LblOF=n%46=6$b zo>;HPD#pfq7i2!OYZI#g^C<@K?!_tfpb~;vF^#z_4eAnbPeloIhxsW6@RY-p`XRic?ym(7M%1hjo?a(x77miRpb$O7>0)fI48@xZlvA*=#*OQTM(^2`T00~8NP z&bS6Q+J+SowrNOV3z`f(#Q+T&NVt`-@-*WQ8c+(y6Ev9S+psZDsXqZ4RaVji^$X){ zAi-qA%E7$4{scUjc-WY+#bgO6CPCM+L1PkpjvFkRA)9HJGdHo6g8INUCs=t%i4knU zRssn&aFS$To?P#RJ4WEzDT)zjY6p7}YmA_#WNHK_QpP|HPV5;&k6FitRm2dKF%Yp2 zj!VppA;iWk4?Yq;5mc2kE7`EJl(Vujzh?m51iY#K2~q?_Aks1D_U@=_*wZn^5hBK1 zc!HIO*%dsi9XA1z`ap&D>iP-L)CbCMkP;0Zr=Xk!EzzK*6gZP2auPJ&kiwFE)6tZw1^S3 z>6*<3r1Ni`9;-BSD`NyJH}iZ@*~}ag0Zyaf!W*<~CkQk)ss>G?UZBp?Qt*lD63kMd zvVw#8QJoFQKOhH2fQ*!nU;{0sVV(kBjPaxnVjeinV=<3|jaeL|0#X}7N-mI5YaodW zTxm?H-vu!kRCOX0i?A^tW8e^C<+%^?DOYtXQ~%QaRJW>uI8 z*I0SjknQ(^c;E@Bpq>H>SvF?HCRow~t=i&XzFD)2l}Dddgn0%dXc;&tKYe8tVovb_ z#TKZ~@3V$gmn{OcIKr6C0KH1*V0QV$#vH!}O8|nr2d>ackh}+~PYhT^cO!WZoR49S z16L0q54-GQm0&)@0JR%Zn<6D;q}miYP(YCe4RcTd1uBE!AsYdT7to?OXs||rQaUJR zTuy-Mq&2J@%q|l^>(W4;Uc<^&~6zDe&dwl57U70&EeiN?xqmUaTf*thN!XZfUIk5zIU5 zz&ZX5BWS@BH}m(p2$Z%TvBP zkyV8GMcos4-GQ1f#o3szGJ3I!oMM%22H$7%Po#;JpAEF?j{$VTELiNCm<=mGNJxZ@ z`A`<9$=3whiNePGoD0-|<7b{z%H+rhTI6XB5@&wO1PN7e zrLo$kvAU$OdV^bJtLoRVfe!{?W1c9+p}_WurId~NVAdxlDNx54B`hC9!g4og)J2>5 z7$ayMq6Bjk$Py9epLO7Nrw3@4KJ#G))W$Nd)+D${7e{L%dqJy4P%{qHLWGvDYe1=P z4XFMCH`Ly5zB3vPsi zZ3b89anP~>JSQ`y9-LAX(_m!-D1HQ)Ki5F!&F_nVuAdcWUY7?F=V888!*PuTbp7md zaGRTzc}blOs|0gH{S!!00cvFVz>5k{>55)@feHuE#`&lw40j5!F~4D$0P-VfJuK9Z zUaS)HAw3I7QGnU72DNG+{zsIq1Z_YHMQ{#)g+mFr@Hv5OBed|b0eK75C&5;aA~iC+ zK+R-OaSW?Bp}`HRyQ8il6$FqYCQwr7J4g!MP{*MF>9KJzhk#NSa)t%>X5bZ~J{$8V z#u8Q$=HsAL&CSe}hL+R8H7O*gvoXh~v6`?kTY#5jd3*x(Agm%-mFKexvZb-=gI1bK zuraqV&Vwu>t&0Fv55mk6^H>EZvYN8(0(m}d0xORYtswnqXtshk7C{*o+E|3vusE|-2~x&|coCd&K^1sm38;L76i(1= z1@3Sw(mGqQaVUV=XrStrfsHv4TT`?Hyzz^639RDG&lxAM zvU2#c@-R=So50F32~=}}S{1ArOq>{%(qxFU{?|T%tgHw1gt4ux2RVxad1XBt^Zu$d z(9+Ojte~=)h53FJC>KdEgU)6tWrenk^;ub&zcWr?62P!{9>n4=wM~$f^Sg*#IS(?q zDuPv<`7a}=C~B;00(p^vjXCBC$Tdrt^sxAHCB(9qV9RDOaX>7aN^qGz+_DZPkY%82 z6FDYA73&gT}wQE@QnHMv0fFhEgxeII=sG$Mg698JhjvRxaO#xurI6w`p z$xPsD=wH`zc)@%JUfa*Si%E}(2O})^Lu|ZUyN3BKD3O5D7VahekoEd_m-HhxDr1)HUCjK!dd+kT$0-^047T zMn;x0Rvs?q{(6oztQ;KnEV9hoxIx=fOqhR`f;Js6urbfA^I|n&ehgad&OEmcbYdOk z41Vys$i1Z;X>6cLBhZ2g=v)%o{T0x;B*^_0dMstEa%{}UK*va(WC6wWK2UYW;(~Hy zW&|rQ8)%+LijDa&Gia#q5j%$$OBt&Yb89IFXk!5n^A%=rA-flJ2}v2Kf%S=@32dJ5 z1XdOoR?s3qSEvQjT3LimW2cj~GEw3H9BIx;3mUE-Z?y9LyIP zK`s5PM0pL${pxqjhY|KYxK&_&iHBVT|SQVJRr0)VRBf29FQq9A> zGy~L!h+yRbU6{_m{I(91Or+SDr!$tYF~0;29k4OaWQ<@H;LvB~VXowu$I8!!q&fm* zC0I3hx7DtC(C#aIK?_=W1=;`vJ&Op`rN?%316t6Qf%q5eI6$jPnU{ce^stJsH8J0* z10_}Pl`u9euB_V3ze+)8Th0Lsi?T7dvw();-?D>F!KMp$AIsY|OK2IUq-sLC+u3W8+X@DPuKc1M32Z)tY(^ z64N2BIZsgFYyq_-AveXUu!1H#K^ywZSe2Pq)^mVc2{%B?lHrH-vVcQ$X+1dgu`wTI zI04R`vY_m|j2$#u$1#r?vOpTD2b3-tKpp%D7FSRs^(+I27aPYK&<@zwj2ur`T$!iV zF&Q$lII_wy^KpPuH&nGOD|jh1hZkrqG}2A7CqS#CSjs>f=+-l?VPn2n7s2An2HNP$ z#yqR;31sRErOe#K$jD;P%EHBbfN2ejE2|ju(;CpCFB9g2j7_YnY`Z{(5F7KGx->|{ zDKq?Gt$IQi{08+txu#{sLq;N;E^a1!d#AZ-o z&3uVLk5$wGWDWCKuq`M>rx!E`IoOz&a)W})lKEU2s1d{A4YpuaJ+#(FF@FiT@@Qsp zWtC_42Dz6*52Tun*=!!9jz>{_8Az4r4>p(iJHtFILE8@A z+X7Per4FL5ml2z~xlnawtUSzH>-1P6m^apg&s_!;`>YIXrQk4z-Che#{7o!ntX#~m zOTo?Y)pZ=;=IS*@FOWZ(Pr){Cq4;w%*d9=*@Gy7Ou3?E_KEc2d!HQN{p@q0BD;M+4 z5~L9S1Z%pWm<`!+zZR6`nHy^LSiw175i`ew3P^4?=C6#PV2%JemW{cSu>|bHUo|*V z(e-kU32e+;Yj(kMNdy~51bQZ65&^YtP#m=jyv4>FoXlp_LIU<0D@zdB;rg5jx?&r} znv+m#TtTag!Pa~OHv;h51Ila+Y|PV2N+5x7h6P*FxDGWMlr(ph%n!j9S~JftXR-$^`{H1J z%@M)E&kCxVK?{Q*H8ZHjZLDX~2Cv6qUQq|C(a+a`mIo7c+z}gSOFai0s80ea=gTv}GXrDl96}ZZ0VPo!QX3~J(K7;sz(DS*1FkPDy_keUnHJ05{ZNS`-#n}Wm9?u$ z_U==#cR`ii-Wo_{cZ4|tOQkrC9khl7RQ1a-udCK$6`csK`uEj60Yww&D8e1}deBNA z+Fugo@C2u_NzB;7VjeqaIZQJ+Ge57n2FlFLKdL{m@`IYHAeV43&t&!j9j*c%!$3)p z+rf^37Ea~JUCGbjgQ2}y%9z_2^}rLzzw4SH-a%0}0o;-Sse8)+Q8%T24Hk7BNOkXK zvg%&YB=&Jw-HYM|Nc#bj8dl@0dqE?gCqNxgwg`}y*qFQPA*o?$Etb>(vTzN!=6yg+ z&5Pos^+?|Bfq9ptoDQ=69airi0h^ZeHDvEDfqM5JwuT1C!pmUq?j+f} zH^EMV22(7aBvcW1!TH%d^x1Dg%;?yVZ+ypHKzP{HyNyuw)`F2SKs91YZ zO++b<68gu%E&};hnz>AmmBSEp>~A#(s4aPdfrI2IZexKKVJMbdgj?dqTn@S^?lWi@ zi1g+;C}1aGwc!rj25II(s5f4d?~U15EqM;N#2sV_5A$R4y|E0d4WMh`IJmr7K2gYIjY%*x1;!^*<^sbT_)HuE3G5>`g& z?n;n)f&Gx<5}7A5F>*1#tL0e3%EEktg#$D$&%^wm#fC+jm7n=$#ROIs<`qn!7AR=2 z7bbqByosffRf1WUV-1ToD+}`+W-nGw<^nHPe&(ySpaB8U{ULmy;WQ?5VbIvfv)Tw& zUglX$po0;f)uypZG0$QGHJ+JIF{ZIvGV^eNj@wuT8eOtve$5El6~@hcUK7+yZDP@8 zWnf+t1iG<@n~nLp?g{XkQ69Ez7Hw87=6xYeY|J1Nh1rB%6UbpTpb&ugpMimS0l3JtWo6}nuG&SB1%)*Dh__P6%2SY} zz(I&+W>Cdt!o_^M){D)H`D@(-R%$;#di#E(F zgY;OmK`PfUdV!acufVDf?C+I9;FCf&F+YLa`~k8ZVmAW=^EPHiR%I^cE458*UN+2M z>p+X0#o3tubAXl?XoG^iu^e<#5A#lDCPzj#=HoTHm=rJ)%wA?jR(&q!vjj~!05RnN zR#T>dkBkE!nDD$Bd|D3ke9+Ms533cJ=dIg!=+?WjZK;8Q7R_S2HOwf^yw5 z&<$s|suh@*vFu`o(pa>v0v93R1a<|cbuCEiJo6ee+D{i=3uQJOAea}i#GF4 z(19)oLCbU5m|qHlkJM*gQ_Nw*#8EZU&KzIQAU zti0)ng!urLx;j}w$3rNxDl*@w1&s~sGyh`d@M6(sHD_K|#{pWxuFrgl5hTFPd=tDB zMvwVal^&}i8}nmOE1r1~lOE{gxiruW9~<)oCQ!QLV1C5}nn2@Wo>uF{yaS}2`EM;k3FmKFCFq7jRCU2siL)_(%1dJbT{n5F+J;4wdAGP1ixKnw3{Z(A!n{`^f<>E^ zm-%D17aQ{qSV)5gxM3m9#@x%Q$I6-xHeyl@D8L!mm><-E4x0t#a8MHEXGRp&^H{W5IhlKyK~qu!9G^f1whb!_v)~g}PUd{D<;+uS zKy?M^EG+QpbXTjux8xk>1dmSaECZFrpb-gHaJ=_afmYXkVYOlI0rllIL0h*V7a()! zu>>&xFVSN$V&z~yQe?vtz$(wYhZmGm8Q7Rt6?%aMuJM5agMoQfAt+Wg*qA3vfezU@ zTm$Njfi?~^{}W3Cm4a-{Yl1+V!@wyFBw+*FQLGI*9*PAVxXcf#K;{cDFJQG{

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 000000000000..c63149a6819c --- /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 6490a2107d5a..000000000000 --- 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 1d86ba584411..000000000000 --- 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 60c64ee8146e..76911b0c565f 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 190/846] 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 4c89647a5741..9519fa6093bd 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 c63149a6819c..7cc4e1d736a3 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 e69de29bb2d1..000000000000 diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index 76911b0c565f..bc8762df593b 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 b5e3dbec5727..6bcacd0be192 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 191/846] 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 89f55986f634..991899f89bcb 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 192/846] 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 991899f89bcb..021fbe932d89 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 9519fa6093bd..9c8035f54a92 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 bc8762df593b..2d4f71e5d532 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 193/846] 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 9c8035f54a92..8dddd6d716d8 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 194/846] 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 8dddd6d716d8..1e1f27e9430c 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 7cc4e1d736a3..ecb55d8cb48d 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 2d4f71e5d532..52dec0e44b30 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 6bcacd0be192..00c76c19ea9e 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 195/846] 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 1e1f27e9430c..089ae79b02c2 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 00c76c19ea9e..83c8ebe6ab9e 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 196/846] 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 089ae79b02c2..7e3ce07254f7 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 000000000000..8034c56b59ad --- /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 52dec0e44b30..7d415222d64f 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 83c8ebe6ab9e..6feda8a52226 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 197/846] 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 7e3ce07254f7..b845b20d2c01 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 8034c56b59ad..91e34c62160a 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 7d415222d64f..9c3c50c5d49f 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 6feda8a52226..f8c86c8d9179 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 198/846] 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 f8d197c15e8d..6846658b6e29 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 6bbf79edfcf7..9c5fe02f7519 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 199/846] 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 6846658b6e29..9c2c96203c03 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 200/846] 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 021fbe932d89..19e1b00050a6 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 8c281126c32b..4d3682263f14 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 cac3cc6bdb31..edceb7551800 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 201/846] 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 4453ae5ce441..1956effa827b 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 202/846] 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 a2e87f5eb374..c797bdb14db8 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 1956effa827b..5ef8d3cf8097 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 203/846] 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 c797bdb14db8..f8139f36369c 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 204/846] 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 f8139f36369c..2c17c4203ae5 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 5ef8d3cf8097..6faa5e9fe661 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 205/846] 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 81ddc8c0067c..e3c6908b76b1 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 000000000000..de262199004b --- /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 206/846] 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 dd236535c18a..42200385932b 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 207/846] 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 9e8e0ff30ec6..418c4ded544c 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 ca9d8d16b787..3d87eb061815 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 208/846] 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 adc945a69441..d6186c319d07 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 021fbe932d89..8b5e0b84eb4f 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 000000000000..9bc8f1bec1bc --- /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 cac3cc6bdb31..a9cd5469048e 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 7ac368878ab7..4171d80f48a3 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 a608601039ce..16a0fd090ad0 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 3cf2abd5b6d8..00bc41c15e9b 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 000000000000..4a1452b25f34 --- /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 000000000000..65c7e6e1ea55 --- /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 000000000000..e4a0320cbda5 --- /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 209/846] 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 3d87eb061815..cb15bdd2f1b2 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 44123829fb0f..70dbdaece8b6 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 210/846] 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 418c4ded544c..c41befbf147b 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 211/846] 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 b637253bd026..79f7705e281e 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 212/846] 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 ab6865bf0f3b..e2672e02b36d 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 346972b7bb4e..ad5b8e4857d1 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 c1f55d7601e5..09468eeaea4b 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 213/846] 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 b8415fa3af12..9057de99029b 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 2c699998ea90..928497ba5e43 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 214/846] 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 b12c3c344ef4..2a81170e49e7 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 215/846] 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 45809b359866..ef3b1da1b0b0 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 216/846] 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 19e06ab66c42..1ad631abe91b 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 ef3b1da1b0b0..5331bf26e050 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 217/846] 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 7ee298e9833f..b95b12c4eb52 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 fc8cb0e747c7..6ba5cfb1d717 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 51912dade035..17de66f9ad0e 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 b58deb7954ef..1b9a4b40a2f0 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 000000000000..c8bc5458067d --- /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 000000000000..75f37ec15c62 --- /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 000000000000..c32292f0eee8 --- /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 218/846] 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 710dec8d33fc..acd628bbaca5 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 219/846] 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 59af475af175..77e90eeac495 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 13e85fda8ffe..7838e8e8ab77 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 83093ec51bd9..ae1aa66be5cd 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 d59a2f1bae03..98de08f79f3d 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 e94eebb88e49..6ac6a12529c8 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 8b58bbb5e657..910b665ccb75 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 6a7a1f1ceaae..0492878fc272 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 220/846] 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 ae1aa66be5cd..3874b040b132 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 221/846] 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 15006a07b44f..000000000000 --- 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 000000000000..9aef3ebe637a --- /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 000000000000..d8f0c44148ca --- /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 000000000000..bd7dc0ac95c1 --- /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 000000000000..3abe76bf2c49 --- /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 000000000000..70445d7ef250 --- /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 97aa220afea5..137a73630940 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 222/846] 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 021fbe932d89..25c1c2d73b6c 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 4453ae5ce441..10f56fb2070f 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 bc43a34ed5d4..6739a4cf2245 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 223/846] 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 ae1aa66be5cd..9020b47a146f 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 a9e6fa329c0f..cf71c3144a2e 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 8e0cb94317af..146ac4b09d5a 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 73758b7eeb7e..e919b1522d89 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 10f56fb2070f..3d12436e9af8 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 224/846] 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 19ad7d92c2b3..501220f28e5d 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 3fca66a5792c..decd3a79cce1 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 225/846] 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 9bc8f1bec1bc..6607a26b130d 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 226/846] 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 a0947608e607..1b65a01690d6 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 000000000000..b29c555eb1e2 --- /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 227/846] 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 1b65a01690d6..76ffe6f6a1c4 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 000000000000..3305ac9bf8b6 --- /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 000000000000..88bc2f51dd66 --- /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 000000000000..43bf8851fc62 --- /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 b29c555eb1e2..000000000000 --- 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 228/846] 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 214cf0c130f2..74fefa65612f 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 000000000000..1f0ca101757e --- /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 000000000000..ca39a654b4fc --- /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 000000000000..ec2faf4ab40d --- /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 f1cc5b564c1d..2c9d4d39e6c7 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 6176a2bc4647..113fab690095 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 229/846] 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 a88f922d8e03..8f7e06b32ff0 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 230/846] 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 0c80394f03e3..fd0cd7a1890b 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 231/846] 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 d6186c319d07..b88044d6ce84 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 41f125d48398..dd4dffda0f97 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 756b6b9a8a46..24eb492ee72a 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 5a43a1a07d24..8d27e6282f13 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 91b65a43be77..908d063729f4 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 39ddf6f226d9..adceb4b6369e 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 232/846] 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 dd4dffda0f97..bb9d8be5daee 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 24eb492ee72a..50116a95612e 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 8d27e6282f13..5119fb403372 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 adceb4b6369e..ef4b89eaa6de 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 233/846] 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 4d3682263f14..169a486d1eb9 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 000000000000..6bd35057bfad --- /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 234/846] 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 000000000000..7e96aa36feb4 --- /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 235/846] 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 6e8ca647dd7a..818d8188a787 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 50116a95612e..d31a597b66ac 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 9c54c3cbac02..7405edbcd196 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 3b7e9739211b..4c3462802e92 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 5119fb403372..a2998d741304 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 188a641aa1af..3eefb232780f 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 a4efe3d15a95..a80e2bf31feb 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 b523fa5b711a..9767e5bf76a8 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 236/846] 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 d31a597b66ac..38f8d007c72f 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 9eb2079c3ca2..650edbb4b11c 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 c41befbf147b..de425211e38e 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 53970af41079..6fbba01416a8 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 8b538be762b0..60b994548b5c 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 4264e2e10bf1..6a1073a23f69 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 237/846] 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 ae58f0a1521e..34341594c198 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 60b994548b5c..9f1700567d1c 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 6a1073a23f69..148c19c7d070 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 238/846] 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 650edbb4b11c..0ef70311fb1c 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 9f1700567d1c..766190f20997 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 239/846] 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 b88044d6ce84..ed8f16e65bb9 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 d31a597b66ac..4890349999fa 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 000000000000..a07279c92b00 --- /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 a2998d741304..6402efc25212 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 000000000000..9b4f2f1f5793 --- /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 000000000000..3421d568365c --- /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 000000000000..065ea9fb9b5a --- /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 000000000000..d285c93782c6 --- /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 000000000000..417b1c107c55 --- /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 000000000000..d428e85b0c91 --- /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 000000000000..19504a051d8b --- /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 000000000000..edd0074d00d3 --- /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 000000000000..e89917c41e8c --- /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 000000000000..110e8421a5e2 --- /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 240/846] 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 a07279c92b00..d3f65db1916e 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 241/846] 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 110e8421a5e2..5a5186bd4fcb 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 242/846] 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 4890349999fa..3d8ce10aef39 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 d3f65db1916e..a6079a8b5bcf 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 243/846] 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 a6079a8b5bcf..fcc9b16068fb 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 244/846] 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 fcc9b16068fb..9fa5860ba300 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 245/846] 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 9fa5860ba300..8587a79e8217 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 246/846] 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 8587a79e8217..a49dc87c0b47 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 247/846] 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 ed8f16e65bb9..1a081bb85fea 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 1d5be893ffba..cd91e7ceb32a 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 000000000000..f092f1297c1b --- /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 6402efc25212..b499d565fa7f 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 000000000000..3aa895120d19 --- /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 000000000000..7d029b0bcf48 --- /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 000000000000..d6cf08367334 --- /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 248/846] 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 f092f1297c1b..1edec1cad6ec 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 3aa895120d19..343e099b2b77 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 7d029b0bcf48..b0c203f06375 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 d6cf08367334..656cfb2f62ac 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 249/846] 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 1edec1cad6ec..66971ee02620 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 343e099b2b77..80b162714aca 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 b0c203f06375..7c43fbea373f 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 656cfb2f62ac..b932fe597590 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 250/846] 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 66971ee02620..4e501f4ca02a 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 80b162714aca..695a460cc4ed 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 7c43fbea373f..6f9d506d3470 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 b932fe597590..9240d3edb27e 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 251/846] 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 4e501f4ca02a..208aa550765a 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 6f9d506d3470..dee80d26bd97 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 9240d3edb27e..7005850efaf8 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 252/846] 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 208aa550765a..6e8d7515671f 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 253/846] 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 6e8d7515671f..be0c44cae343 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 254/846] 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 2d0855f68955..cfad9d79f2b7 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 f911cb68ea57..7e44618e90eb 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 26d96428771d..f57fa830adcd 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 7ba43562d7d4..ea2e23bd3a19 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 d821b5134841..8d48f39a0454 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 160304865c56..ddad16e163e4 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 fdcba1105428..97909c97fc72 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 0a2d577396a5..a3d1a500aa7c 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 6a256627bd18..5b0f9d6e3ec3 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 be0c44cae343..b6d08b8ae17a 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 3c9397441735..4d54e3117faf 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 7da47ee4ff94..de94fb871472 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 b1345f0de5e4..df87c1b98025 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 f85db1e2006e..eac7ae2358e8 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 4dcf6c105ec6..2d72f9c3fe17 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 99ba7d64331c..6f23e9680068 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 99413153d49b..7a79741b30bd 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 8fc97f2fd64a..20bea3cbabe6 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 732f4b28e06e..3b10b7b82a08 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 255/846] 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 1a081bb85fea..01c0e4b0302a 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 cd91e7ceb32a..fe34e4390d65 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 a3d1a500aa7c..6ec7f8cae5d7 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 b6d08b8ae17a..c877cc6fa8b8 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 4d54e3117faf..3c9397441735 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 b499d565fa7f..e681f47f949d 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 256/846] 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 01c0e4b0302a..1a081bb85fea 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 c877cc6fa8b8..7e299dd5fd99 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 257/846] 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 cfad9d79f2b7..c4397560d7db 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 7e44618e90eb..5426e14ead57 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 f57fa830adcd..1b09328ceabb 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 ea2e23bd3a19..a36fdca5d5de 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 8d48f39a0454..b803d753b6d0 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 ddad16e163e4..f1c8894c0ee2 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 97909c97fc72..75e123eb5939 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 6ec7f8cae5d7..5eb8398d68e5 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 5b0f9d6e3ec3..3d4225f36a7d 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 7e299dd5fd99..ab9ea76a8389 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 3c9397441735..faef7e724dd0 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 de94fb871472..f16db2df3a92 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 df87c1b98025..d6f31a99bb36 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 eac7ae2358e8..39a8c0208728 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 2d72f9c3fe17..4bb4b087c556 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 6f23e9680068..3a3b79925ff9 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 20bea3cbabe6..0ac7714fbeb7 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 3b10b7b82a08..063f94582b9d 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 258/846] 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 99505fc6b29b..eb6d495acbe2 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 259/846] 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 5eb8398d68e5..2e5f5f10f4b9 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 260/846] 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 c4397560d7db..d68d0d8ccf58 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 5426e14ead57..fbd2eeacc6ef 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 2e5f5f10f4b9..c8aa98d34892 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 ab9ea76a8389..8dbe58763bfb 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 f16db2df3a92..4cdff63f1180 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 261/846] 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 4efd06892679..e3ee4296119d 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 42c5e5d2bae2..7dc7ee94affc 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 8be0d9ad56fc..01181484e7de 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 943fbdb0b832..900dc2b79336 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 78a9d44829bb..b0641a100cdc 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 dbc1cac5cb43..a0a2c973900f 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 ad5163f0079b..2422eb911e90 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 000000000000..0ce6f72535d1 --- /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 000000000000..c87e836bedd9 --- /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 000000000000..ae7bbaa44738 --- /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 262/846] 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 e3ee4296119d..3b2e46a9a852 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 000000000000..f90695bc3fe7 --- /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 000000000000..e7b048e262fa --- /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 000000000000..fe1dfc7a4510 --- /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 e343c37740da..911700bab004 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 810f929c8568..d202385fc8ae 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 2dfbf562d15f..ac8d0c0cae06 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 0ce6f72535d1..98766e68aaf6 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 c87e836bedd9..3c4b636a3d84 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 ae7bbaa44738..f370e24bf052 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 263/846] 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 1a081bb85fea..6261ca4879a5 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 b754a45aa404..34aa2981535d 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 d8d16efb978a..000000000000 --- 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 e681f47f949d..888b47554846 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 c28cca144ca3..88d3b0e74900 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 92f40fe6f1fb..f279850fef8a 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 000000000000..2d1ac8836d63 --- /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 000000000000..376abf9244ea --- /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 000000000000..0b32af039a8c --- /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 ca70e3b7148e..0cee0a28fc7c 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 11dff94a2886..bd13cf1bdfaf 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 1d0ee82b4f77..94479e685551 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 502615fb175a..be323035d6cc 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 8b4e2d21331c..ce8582d2b221 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 b0904e41b6f4..a3a9aa40e3b9 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 51a6f4350d32..25d1476062e7 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 8a81e92f04a7..de3fe00d5fa6 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 1cd515441d13..b77969d53d92 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 264/846] 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 3b2e46a9a852..9f241c2c3a2b 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 000000000000..13962a272d45 --- /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 000000000000..26bc20d5370b --- /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 000000000000..fa956b9139eb --- /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 265/846] 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 bc6e868823f7..3d48bf739ebe 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 26476af4cb62..25ccabc1c883 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 ca921dcfdfe9..7839908fe4c9 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 d40cdfcca9f6..32fc01149d88 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 ceed6a74c4fc..fb26b9fc27d2 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 156246fb8bbb..1bd16e6cce53 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 3f030dd84225..63133a4872a3 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 fe34e4390d65..c4f1af8f4e41 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 a79f94855bda..168f9f953e4d 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 b803d753b6d0..396bb6591090 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 f1c8894c0ee2..4c595029ff7b 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 3d4225f36a7d..400f4b609af7 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 ad39e59d0678..b84a1a3fe249 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 bf80b62afe6e..9c9626735370 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 2597f5f6f17e..621ebdef2f0b 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 c11a2ff9ee07..a3521c31a6be 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 7dce23dd2230..04be96aa64cf 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 d8c8eff2c853..f204a0ffb2c7 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 4cdff63f1180..194786c5c414 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 509bbfd27c1a..1aeff1baa362 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 d6f31a99bb36..71207caecf58 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 b9aa202b328f..25d136e564d3 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 39a8c0208728..776c6bc57ca6 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 e19a79dd8dad..58c1103da9f7 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 3a3b79925ff9..0b4cba3fc42d 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 7a79741b30bd..87cb454f654b 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 decd3a79cce1..47315fa64cd8 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 266/846] 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 9f241c2c3a2b..d88e47f396cf 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 000000000000..64461417a6a1 --- /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 000000000000..9046f184b3e5 --- /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 000000000000..81e818215137 --- /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 267/846] 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 d88e47f396cf..b1e258f4b160 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 fa956b9139eb..78354c2f62d4 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 268/846] 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 b1e258f4b160..beb0b234408b 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 64461417a6a1..92480c5db8be 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 9046f184b3e5..062e7c3fdc17 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 81e818215137..a6ffdca64eef 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 269/846] 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 f90695bc3fe7..bbe411b3f488 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 e7b048e262fa..586fd170ea14 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 fe1dfc7a4510..42069d9ee9ef 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 270/846] 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 beb0b234408b..11bd0ae23aa3 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 98766e68aaf6..56762400593b 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 3c4b636a3d84..1f800e4628dc 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 f370e24bf052..d5a5f1bcca10 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 271/846] =?UTF-8?q?Includes=20TODO=20for=20constants=20equ?= =?UTF-8?q?ivalent=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 11bd0ae23aa3..3087d6a940a8 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 272/846] 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 1a081bb85fea..c5bbaac0df64 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 fe34e4390d65..0f362dbf86bf 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 000000000000..af3c948ec82c --- /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 e681f47f949d..078924d3f9bb 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 000000000000..a637c22fbcd2 --- /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 000000000000..d99ca1b5b55d --- /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 000000000000..915eea3bfc6b --- /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 273/846] 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 3d48bf739ebe..bc6e868823f7 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 34aa2981535d..aeabb99a30d1 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 2d1ac8836d63..f3e19092480a 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 376abf9244ea..fbae7c18b923 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 0b32af039a8c..4668f8565a65 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 bd13cf1bdfaf..11dff94a2886 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 94479e685551..1d0ee82b4f77 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 be323035d6cc..502615fb175a 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 274/846] 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 ddc41f89f8de..fa560ffb980c 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 d940776817ca..91c1789a2ffb 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 3a3b79925ff9..93075b9f0b50 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 779fd57707ad..c017d1cf9a46 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 0485a5630afe..1929c72b2f2c 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 275/846] 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 b754a45aa404..a6cc1097441c 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 34193be0b75e..b624a41a29b2 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 59861d46eb34..3a07c2ec5426 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 276/846] 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 9f7bdcb1be7e..69a734e4ee4c 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 277/846] 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 c164ec9aaf17..dd608de5723e 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 278/846] 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 fe34e4390d65..4d9776018cf4 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 e681f47f949d..c20793ecd3ec 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 279/846] 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 af3c948ec82c..436374d7c545 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 280/846] 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 3cfed4125b35..7d034117547c 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 281/846] 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 6b4021140997..19b253b0fe2c 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 f72943a04f25..fb4f1658597a 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 282/846] 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 169a486d1eb9..502bf0c42795 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 283/846] 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 70445d7ef250..98fd0df685fd 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 284/846] 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 436374d7c545..3a0b3b1c2571 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 285/846] 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 8090f4673aae..42bff564de03 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 28048999e8ec..3d1c458879e5 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 6440ff41be81..f56dd65b9dd2 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 286/846] 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 4c595029ff7b..565a08f1292e 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 2b20a4ceb4ab..6c4200a79055 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 287/846] 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 3a0b3b1c2571..a3af369e41e5 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 288/846] 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 2ed1b09bece7..a82b11591619 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 289/846] 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 5d08b44ba404..1c927b5f83a5 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 32e79317f822..7a4ca3902b33 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 000000000000..fceb885516b8 --- /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 b89a87128626..96b004904aa2 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 000000000000..bdb4710cc697 --- /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 000000000000..e63d58746090 --- /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 290/846] 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 565a08f1292e..69e985cc0a40 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 6c4200a79055..29c56f3a94f5 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 291/846] 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 194786c5c414..fab13c8c1246 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 7fa58cf76499..8a831375b412 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 292/846] 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 3f425e5b7258..e0a95fb9f363 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 293/846] 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 e0a95fb9f363..e85e8874ba60 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 294/846] 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 1c927b5f83a5..b9d0a15b755d 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 295/846] Typo: Change Log -> Changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9d0a15b755d..776b04295f94 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 296/846] 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 10f4694640ee..9944b4096bae 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 dabb695368db..f91ccfaed743 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 72319bc7e458..37c11d72a574 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 297/846] 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 9944b4096bae..6379dffd22e3 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 298/846] 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 818d8188a787..c17a0e833305 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 323cad7fa1a8..7a3f35aca0ac 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 bd7ca0388394..9fb10c7f6276 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 7a4ca3902b33..823afdfd289b 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 bdce1bf15218..1ad184dfc460 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 59ccc6333fdc..5041af750cee 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 8dbe58763bfb..9922d906118d 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 87cb454f654b..5e8800d38eb5 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 6bb1032a1729..27222cc0869c 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 d50c919188be..6a0f1b26c883 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 f278ee41aa33..a1904c904d0f 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 cbc4ca391616..2770eb2b2ab4 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 17b1f1bd0bf3..4d284ae1319d 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 2d0891fd3c20..2d08e82f349a 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 0263e2f5f20c..7ed27439ec6e 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 299/846] 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 cc5e21a7ca6f..7f90aedf1619 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 300/846] 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 ce8582d2b221..adbff8af8d9c 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 a3a9aa40e3b9..4c2870e7803c 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 25d1476062e7..d3c9ceaa3d7c 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 301/846] 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 bd474c208070..fe00a48adef0 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 302/846] 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 1d4772bb3d60..48ab98418e4f 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 303/846] 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 6d1d083fa8d4..502fffc5e6c6 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 1f5b98118870..60c2f3ec9b65 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 304/846] 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 b84a1a3fe249..125df226ceb3 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 bacd67db7c30..0f2ba4a075dd 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 68c1416bb6b1..d5e0664319b8 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 305/846] 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 823afdfd289b..f371942dbeec 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 125df226ceb3..29aba7c12187 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 000000000000..391d70af1599 --- /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 96b004904aa2..1879aae77fb6 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 000000000000..3dd365620ccb --- /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 d5e0664319b8..0ac97ae25d09 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 0f2ba4a075dd..21ee58e5b44c 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 000000000000..0aebc3c419e9 --- /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 306/846] 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 391d70af1599..9477e79f567a 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 21ee58e5b44c..e29a1dce0c7e 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 0aebc3c419e9..0e000fee85ac 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 307/846] 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 9477e79f567a..8aa478ea2d69 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 0ac97ae25d09..79f276634619 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 e7737f9dd856..644161d9f5d8 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 0e000fee85ac..7c8865f1bd37 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 308/846] 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 fda7480194dc..7932be0d4b1f 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 4c3462802e92..a515ee29c82a 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 764c10a6d398..cdeefda4c234 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 839747b131d7..acb7ffb305f2 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 eced198283ce..89b392542991 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 309/846] 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 fc10e5077b83..26a1c32b6b3a 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 310/846] 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 69a734e4ee4c..dfc5cc077c37 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 8092be277cca..d5f4f5d56591 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 000000000000..c1fd2fbcd1bd --- /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 311/846] 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 222b81023a77..a2984d736416 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 312/846] 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 d5f4f5d56591..168092f7329c 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 313/846] 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 c1fd2fbcd1bd..5c07d9b98a5a 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 314/846] 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 97cc58023f55..56686f6d24ff 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 000000000000..928b6acb9510 --- /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 a65ca3bbb131..7075e2598ebe 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 5709f3355298..feedc2f288a2 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 315/846] 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 a48ad3185e9c..1bf37632e326 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 fdd4bc581f30..813cdaecaa91 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 4cae745e7c02..540fea23b36b 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 84ec53702788..b958b0354520 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 316/846] 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 776b04295f94..a17807250443 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 000000000000..7913aab6f24b --- /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 f371942dbeec..6d6dd06cc216 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 1879aae77fb6..00d3df8f94f6 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 000000000000..63687e7b3dbd --- /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 317/846] 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 59c62f1ae944..627475ee1d92 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 7913aab6f24b..000000000000 --- 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 6d6dd06cc216..996aad31d3e1 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 00d3df8f94f6..011504710e13 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 318/846] 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 63687e7b3dbd..15f66b7a9c59 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 000000000000..30404ce4c546 --- /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 319/846] 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 627475ee1d92..4f69c2d7af77 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 320/846] 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 4f69c2d7af77..04395621e9ee 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 321/846] 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 30404ce4c546..66bc4d42ce8c 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 322/846] 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 04395621e9ee..ab001f7773e4 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 323/846] 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 ab001f7773e4..84566252abd7 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 324/846] 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 84566252abd7..16a6f0c20e1e 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 15f66b7a9c59..b82dc518a3ba 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 325/846] 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 16a6f0c20e1e..820ce85cff28 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 326/846] 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 820ce85cff28..cdb748de0c0a 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 327/846] 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 cdb748de0c0a..08d8100a8854 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 011504710e13..119908b3cc45 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 328/846] `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 329/846] 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 776b04295f94..0ca4d88ed383 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 f371942dbeec..07ef087c2b04 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 0ef70311fb1c..6bfdac37180a 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 1879aae77fb6..9fb3dfc96ec5 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 000000000000..cb2b0054e352 --- /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 000000000000..027e1c752041 --- /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 330/846] 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 6bfdac37180a..10811374875c 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 9fb3dfc96ec5..197eab759f11 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 331/846] 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 9edcdd979ff4..3f62a3cab1c2 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 4171d80f48a3..684a28aebcb6 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 16a0fd090ad0..05789ee52323 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 00bc41c15e9b..d2d15362a6c3 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 332/846] 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 3f62a3cab1c2..9217324b18cc 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 684a28aebcb6..a5fdf7df613d 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 05789ee52323..abbc4e16e567 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 d2d15362a6c3..b6479cd69eac 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 333/846] 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 c29432bf9338..ed02763397a0 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 7a4ca3902b33..b336de37c61a 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 168f9f953e4d..1b3639a02a0d 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 396bb6591090..d0d6c2e04a05 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 fab13c8c1246..97bd52e517c6 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 4b7a1c2b537f..39317ecc82ef 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 334/846] 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 1b3639a02a0d..4df6827d77f9 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 335/846] 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 c19fb148cda5..864d1ea87f57 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 5c1fdd83efb0..0458950edee1 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 8c47cb37d8c9..21bf49d16f04 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 27222cc0869c..4f551690c437 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 6a0f1b26c883..6ed60309947a 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 a1904c904d0f..ccd828674276 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 336/846] 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 7ee603afeb07..1577e7a46edd 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 337/846] 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 7e3876ff49b4..231c440463d6 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 be37dc16b9a3..60a3e206283f 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 1577e7a46edd..33a1ea360959 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 9113aad90dd7..bb67bfa83e91 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 338/846] 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 231c440463d6..11a9b1e531c8 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 339/846] 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 60a3e206283f..be37dc16b9a3 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 33a1ea360959..7ee603afeb07 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 bb67bfa83e91..9113aad90dd7 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 000000000000..136af42a9fef --- /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 000000000000..136af42a9fef --- /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 000000000000..5058c171ac23 --- /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 340/846] 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 11a9b1e531c8..2181304f0064 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 136af42a9fef..163eaf965dd6 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 136af42a9fef..163eaf965dd6 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 5058c171ac23..700c73b0b223 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 341/846] 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 2181304f0064..c931c212735f 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 342/846] 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 c931c212735f..e9ce804320fc 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 163eaf965dd6..000000000000 --- 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 163eaf965dd6..4cf03e820352 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 700c73b0b223..0c1e61d74966 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 343/846] 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 10811374875c..06631f89f27d 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 344/846] 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 eb6d495acbe2..911da40d27bd 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 345/846] 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 776b04295f94..43a32e828d86 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 f371942dbeec..9fc07e07fd36 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 000000000000..c48da004a60e --- /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 655b1133cf74..c75f8042907c 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 1879aae77fb6..41d06a678811 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 000000000000..8f8f56659315 --- /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 000000000000..f9bd97790671 --- /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 000000000000..b0b729ede48e --- /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 c017d1cf9a46..31c2ba0f9c58 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 1929c72b2f2c..a3c8ae468ede 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 903b6e5099ce..70c6cf0a3b63 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 346/846] 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 d3b351f30ef7..3bd73d9f21a9 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 a515ee29c82a..923b319d7778 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 29d9139d3e34..9e77dcd87316 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 5e85d091a2ae..41bcb0a189e7 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 21e9d4048a58..3f1cbc17e72d 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 347/846] 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 9edcdd979ff4..ff8da6b4015a 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 348/846] 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 c48da004a60e..cd7056620a2e 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 349/846] 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 43d83d978b8a..fbc783f1c2c2 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 26aff6af8cdb..2bd5ae0ed98f 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 6359c20040c7..48891a59c000 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 a08d7da6dcb8..1b10226c930f 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 000000000000..e3a5a647f763 --- /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 000000000000..559cc450b9de --- /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 350/846] 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 48891a59c000..1c2f1225497d 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 e3a5a647f763..4bb5e73ff0d4 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 351/846] 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 1c2f1225497d..766c68df6235 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 352/846] 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 2bd5ae0ed98f..308868fc90a9 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 1b10226c930f..1f79e44049ff 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 353/846] 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 766c68df6235..8ca67cae0e9c 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 559cc450b9de..ddc5d48cd413 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 354/846] 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 8ca67cae0e9c..3a1fbc4bfedc 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 4bb5e73ff0d4..ff1088f86f64 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 355/846] 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 3a1fbc4bfedc..86952c10dfc1 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 356/846] 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 86952c10dfc1..3104f0c137e8 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 357/846] 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 3104f0c137e8..5c918ff648f8 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 358/846] 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 08d8100a8854..80a067589824 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 7bee9c499e1f..690d705573d3 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 359/846] 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 dae39aaf5e21..1f798fd11209 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 8307d4b3019f..90ec5676493a 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 b552c137f7c7..653946dc025f 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 360/846] 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 bfe896d5efaf..179ecee03da6 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 81864340f1a5..80c85e70e898 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 000000000000..050a03467fb0 --- /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 06631f89f27d..d4acf8df46d8 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 b64c6e544095..a20e410f79b1 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 000000000000..12bb84582ff9 --- /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 000000000000..703705c78428 --- /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 361/846] 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 050a03467fb0..b71f2fbbd46e 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 362/846] 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 179ecee03da6..3f470f601eff 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 80c85e70e898..3c39de1abf1e 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 000000000000..1b23ecd9ad28 --- /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 b71f2fbbd46e..000000000000 --- 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 a20e410f79b1..91761e8a86df 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 000000000000..da4bbcf4a7d7 --- /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 000000000000..ee803b24071f --- /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 12bb84582ff9..000000000000 --- 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 703705c78428..000000000000 --- 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 363/846] 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 ddbf4e98c51a..150acfbfee75 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 16bbb1c037d8..ebdb8e749520 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 3ef29dd63880..b5211318a150 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 4493b17a4b4e..d79c300c0744 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 a29b832eb601..d81676a3d9f4 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 8fd0093f4fdb..ecdba921a5d0 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 000000000000..642e48fd1315 --- /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 da4bbcf4a7d7..178abc341a80 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 ee803b24071f..fc21d3d0afdd 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 9a0da404cb6d..96d1c54946c0 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 58041b62df35..931ffc186659 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 1d0a3ecd0ff8..631042c616bc 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 2fe18f923f08..679eb2326268 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 364/846] 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 1b23ecd9ad28..4590128bedc2 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 642e48fd1315..bc770d8bf689 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 178abc341a80..9074920b2046 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 fc21d3d0afdd..227c6d73b625 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 365/846] 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 4590128bedc2..38bdd0f7ed23 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 bc770d8bf689..9da21eb6b29b 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 9074920b2046..17aeaaf97ac7 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 227c6d73b625..f4c645d35c8f 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 366/846] 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 1f798fd11209..004dd50a31be 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 90ec5676493a..f7ed72a11cf6 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 653946dc025f..9f8e26fa406f 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 367/846] 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 dfc5cc077c37..5f7b1e85ee9a 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 5c07d9b98a5a..c81e7f6e0692 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 368/846] 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 6359c20040c7..1729fea7bc87 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 000000000000..68afa8f8c3a8 --- /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 000000000000..a60ca345cf78 --- /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 be37dc16b9a3..7f2fcf02f6b5 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 7ee603afeb07..788a9eb3264e 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 9113aad90dd7..2a9539d59759 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 369/846] 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 6a141f1fc786..28b276967bc3 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 370/846] 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 570ae66d595e..3009aa3a64e9 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 7880cf36415f..3b267b0dab2f 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 01cf487ac148..9b8ecaed6921 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 371/846] 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 3009aa3a64e9..c225a3bd3597 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 3b267b0dab2f..adf81607440d 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 9b8ecaed6921..5105fff8f5b8 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 372/846] 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 c225a3bd3597..a75989e3f130 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 373/846] 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 a75989e3f130..bad1bb7224eb 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 374/846] 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 bad1bb7224eb..a549c3b78ef6 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 adf81607440d..80dd2f744b3a 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 5105fff8f5b8..2a0a43e83a65 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 000000000000..6c5ffe6aba8b --- /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 000000000000..f8d248fc98d8 --- /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 000000000000..2b7d4628c3fa --- /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 000000000000..ed5e0d384bf5 --- /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 000000000000..b6fd43569569 --- /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 375/846] 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 a549c3b78ef6..07e55ab07624 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 376/846] 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 8617445c8a60..f2bd651ab253 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 377/846] 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 7b6bd69ffca5..33c1bbd488a9 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 378/846] 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 33c1bbd488a9..7f58a381adce 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 379/846] 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 f09af0466c01..9d957371c6bd 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 380/846] 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 28b276967bc3..ac1c7aa9bbbe 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 4b77ac551e77..fb12257021a1 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 9b0e7550cc31..c640c82d6d7c 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 381/846] 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 8e44f2ec2408..dc5aa6691396 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 eeb1f20ee894..55d94b8257db 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 382/846] 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 29aba7c12187..38b11c5e7335 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 bc785b075e02..f42b246afd29 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 383/846] 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 dde799fcae4c..cdb49d777d8d 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 436f5d86a062..1dc37fc8b259 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 384/846] 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 9f967d59c8be..223628cc610d 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 2045ffdb5f09..67faa8bd4a0a 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 522f31b72d01..9867e2eedcff 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 385/846] 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 841902943f00..28c1a54d2c5a 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 648ba3ccd01d..18518def0acb 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 386/846] 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 f2bbde28c2ab..fd755dcc7904 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 dc666bab4603..5b62dca629f7 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 e4d085470c3b..46ffc16c23e5 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 4013d1ed667f..bf4b6c930980 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 387/846] 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 7b6bd69ffca5..460d631fab0f 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 b563a2ebec2d..a1f4c70fb278 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 388/846] 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 ea2447681293..874e19d9e9fb 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 453760c6b921..26b2057548bd 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 389/846] 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 5d16192b7543..bae7c4647d48 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 5dcda7b3af4a..05ef4e25f7f8 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 390/846] 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 1eb380a22cc6..abbcaf43f415 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 0e4c9b5682df..40fcad2ab1d4 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 391/846] 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 4afbd1ed0e59..ec3b6afa6300 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 5d360ef30a1d..8208c0ed7e39 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 392/846] 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 3ce110e8e0f7..376ac55f9c98 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 bf753a732f00..594fca44a321 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 393/846] 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 38b11c5e7335..02789735c17a 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 e7b92ce1e197..b8d3c2945322 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 394/846] 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 fe457aad50e3..3fda00403c61 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 34ba167a9479..d0a0f219097e 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 395/846] 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 407527251da2..234ae37612bf 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 47e37aed2464..618c5980d64e 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 396/846] 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 502fffc5e6c6..4e335a0222f2 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 7e42d72c30b2..23d47e3f1ff0 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 397/846] 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 234ae37612bf..a3975683cb30 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 a399ff52fb8b..880467624835 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 398/846] 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 c6f57298c260..7bbcc67aa2dd 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 3c3d2eacd8d9..649831f9c069 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 afc853dcfce8..cc7e55a757a3 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 399/846] 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 4c1f2e8e01a8..f88075798ca7 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 d53c1edecac0..dcb5061371f8 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 400/846] 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 66a145a7f14b..b8583402928b 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 09b18d71baf9..bb8dce2bbba4 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 401/846] 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 4d4a96766548..3c041bac234a 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 ad66135d326b..19e843c2c0a5 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 402/846] 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 6b6c950e0abe..aa550510867f 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 f08bbd6a12c5..ad677f6d6fb9 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 403/846] 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 95613a1b82ef..0f5d5ce3495b 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 8c5d548222e0..193d9f9bcea3 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 404/846] 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 0f5d5ce3495b..4fb899125e8a 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 405/846] 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 fd755dcc7904..ea4b8172c9c2 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 406/846] 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 6395b571504c..dc8779748e0e 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 407/846] 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 5b62dca629f7..33bb5136ef8e 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 46ffc16c23e5..a01f7f956f62 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 bf4b6c930980..416ec1a01ab3 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 193d9f9bcea3..c78560007217 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 408/846] 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 b8dc50816329..be3ae7ab3801 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 fa8c82ae0f34..062d30b262c1 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 409/846] 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 f077c1461831..7b5e92eb5ee1 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 26a47d237065..e662d608edf9 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 98288dde6d84..b6f1e83181cc 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 fd5055c08f63..0205d1ece60d 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 46597acc6c0d..1157b179317e 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 410/846] 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 cd7056620a2e..22c49a20451f 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 b0b729ede48e..b73012a4691b 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 411/846] 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 641e6a170432..1cd5b2012922 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 9eec6928e8ce..4f43cff50244 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 412/846] 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 568898aa5c9b..21efee712698 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 7dac08658554..a3511ba708a8 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 413/846] 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 570ae66d595e..f4eb9c4516fc 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 bbf61df0cda6..8c10a252ee01 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 414/846] 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 e91fb0c2f27c..c629ee05ab97 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 29a6914761c9..c5b020ba8ced 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 415/846] 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 5f931a0added..b57fe8dc4269 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 2eb2023b3b9e..5bb9a606422a 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 416/846] 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 9fb10c7f6276..4e6bb6047854 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 585d32845d29..aab688cc2d8b 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 417/846] 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 cb0fd59a2d40..d73d0f1752ea 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 2835391de7f5..3aa0e4ddcfe7 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 418/846] 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 1dfb2eaa5797..8ece44878fe3 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 bd8adc2c5705..cdbeff6a0378 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 419/846] 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 91214f277be6..48caf48dbdb2 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 71f3f5e083e0..5935eea5e036 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 420/846] 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 d73d0f1752ea..a9294a87f15d 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 3aa0e4ddcfe7..b1d481190ff5 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 421/846] 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 5b22df5fe491..28b20cdeac34 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 334ccb010167..6afee0f36b9d 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 422/846] 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 c11e291f98e4..b86d2e766566 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 776c6bc57ca6..427a1b657731 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 bbb300296be9..ccc9e2509521 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 78bc4d4bd20a..53d1b86d02a9 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 423/846] 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 063f94582b9d..5f88dcb188a0 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 424/846] 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 7f58a381adce..31da45f6b8ae 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 425/846] 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 4e335a0222f2..e026f27d9a7b 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 426/846] 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 e026f27d9a7b..596d4eb61417 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 427/846] 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 596d4eb61417..3a688a7bbef3 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 428/846] 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 60c2f3ec9b65..5c280efac1a8 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 429/846] 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 23d47e3f1ff0..388fc7400820 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 430/846] 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 c3dea4475213..78cebc30472b 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 266358334587..6514fd6d1ac7 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 eaa57ce3024b..92e4f67f5db6 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 431/846] 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 1bf37632e326..4ab2b5e796de 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 813cdaecaa91..8a9b0cd3cf01 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 540fea23b36b..4faa1572973b 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 b958b0354520..f1e880d2696c 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 432/846] 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 78cebc30472b..e0204273197f 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 433/846] 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 168092f7329c..3c782e9b17ff 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 434/846] 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 f4eb9c4516fc..b6266ef2ba19 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 435/846] 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 6ad525d76205..4a4445200a1a 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 ba3492a6fff1..292dbd7ad6b4 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 f2e76442a19b..84e907d7125d 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 e73a791891f8..856771596202 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 3eb960f53d7a..03b8ee816658 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 436/846] 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 5e9ed54c8482..714337732545 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 6ad525d76205..7ae185103a38 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 000000000000..11bdd27d9b1b --- /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 ccc9e2509521..46827084a609 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 000000000000..3b46324704e1 --- /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 000000000000..cbc0a41036be --- /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 437/846] 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 8352a8a3d2c6..f7db2563d2b1 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 ff1088f86f64..bfe27e020445 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 438/846] 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 5e9ed54c8482..3b9cbbf0dcd7 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 6ad525d76205..4f261ba932ea 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 000000000000..68bbef7261a9 --- /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 923b319d7778..ffab0395120a 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 ccc9e2509521..000ab8b8f368 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 000000000000..49607c49218a --- /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 000000000000..bbeebd027b16 --- /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 000000000000..453ec2d42f1f --- /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 439/846] 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 4f261ba932ea..5e4a4a4f49ce 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 b6266ef2ba19..2986a5a59449 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 68bbef7261a9..000000000000 --- 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 000ab8b8f368..4fd327768743 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 49607c49218a..0812c026a644 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 bbeebd027b16..ab293bbe4eeb 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 453ec2d42f1f..0e9bdaa23e7e 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 440/846] 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 2986a5a59449..614773a7e26b 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 441/846] 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 6ad525d76205..d2f66cf9bd01 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 000000000000..a8876619ac15 --- /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 8ed20995a70a..2bd0cccd39d6 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 ccc9e2509521..7464f1cc6de2 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 442/846] 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 a8876619ac15..861a7ec558c2 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 443/846] 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 861a7ec558c2..ba280cd5a617 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 2bd0cccd39d6..593e2f6c74b2 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 ad20e2381073..6b5cf2626df4 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 af0cdfb207ff..1a693c9aa53d 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 444/846] 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 ba280cd5a617..eb0bf12c0ab5 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 445/846] 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 fa560ffb980c..000000000000 --- 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 d2f66cf9bd01..63de0f8a0c22 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 eb0bf12c0ab5..000000000000 --- 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 593e2f6c74b2..4d91f9be9996 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 000000000000..e322e4025353 --- /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 6b5cf2626df4..b795516f999c 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 1a693c9aa53d..3547991935de 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 446/846] 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 63de0f8a0c22..c0b645e9311b 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 7464f1cc6de2..dff6198d00d1 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 447/846] 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 4d91f9be9996..3c5541e64b4d 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 e322e4025353..7548c6afa973 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 b795516f999c..d849e093da7b 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 3547991935de..29f2bd1852af 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 c34eecbcbb63..f73c833a801f 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 448/846] 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 6ce36fd2360e..9555459e240e 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 682d7b3c4ceb..883683e08a2a 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 65d40ee6832f..05c7f9d33a79 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 449/846] 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 50fe7612909b..c21190e9b9c5 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 986e9d9bee4d..cb29f71387bb 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 2330978e67f1..61b7f2647ee7 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 233f95deeddc..1e76163f946c 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 000000000000..fcfa6dfe12d5 --- /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 000000000000..04b3c8ae1e25 --- /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 000000000000..b941bf842469 --- /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 450/846] 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 c21190e9b9c5..54489751014e 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 451/846] 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 61b7f2647ee7..c672ca41decb 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 452/846] 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 c672ca41decb..ed8eaba75d1a 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 fcfa6dfe12d5..c806cf8dce4d 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 04b3c8ae1e25..dfc6d3ba5735 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 b941bf842469..155918175405 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 453/846] 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 54489751014e..675dfd420a7f 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 cb29f71387bb..ae7045884f70 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 1e76163f946c..2a66d3495ed1 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 454/846] 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 ed8eaba75d1a..463ef48f62c4 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 c806cf8dce4d..7f9d90a8569b 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 dfc6d3ba5735..ca8238d6dcfa 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 155918175405..b8ec654e5c7b 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 455/846] 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 463ef48f62c4..6693c358a020 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 456/846] 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 6693c358a020..4578c228107f 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 457/846] 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 4578c228107f..70a6b1f50213 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 458/846] 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 be3ae7ab3801..c506440ed798 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 459/846] 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 70a6b1f50213..bfc89a747421 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 000000000000..0dbedc4919c3 --- /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 460/846] 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 bfc89a747421..c2b2cb980121 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 461/846] 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 0dbedc4919c3..b44089f7bfcb 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 462/846] 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 b44089f7bfcb..a3e7e9971f8f 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 463/846] 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 ca8238d6dcfa..fd8f8ed03296 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 b8ec654e5c7b..e86b7ed62534 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 464/846] 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 675dfd420a7f..f662de122f99 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 ae7045884f70..17501e8e6da4 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 c2b2cb980121..0f50a4c813ad 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 a3e7e9971f8f..31517659c34d 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 2a66d3495ed1..3229c8da5077 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 7f9d90a8569b..fa66e68794e4 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 fd8f8ed03296..04f47d1aa297 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 e86b7ed62534..5c1b2eb1f14e 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 465/846] 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 dab1e96e282f..b3185b888401 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 18529f2113e7..280a2c7fe677 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 1f8bff8d71e0..10a64769585e 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 bae7c4647d48..19f56195ec1b 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 140cd21c34e6..e16ec783fab7 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 93f6ec92ec71..1b02cee126d0 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 fb12c565afd8..dbc676ae2240 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 754f87e6b55e..cc39f060fc7f 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 28fb6ed12a05..785c409260ee 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 6c2356799142..0b8d0bd9e111 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 223628cc610d..530552f79405 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 466/846] 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 785c409260ee..1014546ff89e 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 0b8d0bd9e111..8fa5d22210a3 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 467/846] 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 1014546ff89e..cacc9f8d6f27 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 468/846] 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 f662de122f99..5ce63c0a1574 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 17501e8e6da4..87c297e72eb0 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 000000000000..e096c9aebc12 --- /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 3229c8da5077..bf58c117aaaa 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 000000000000..a7cbb9cd78b1 --- /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 000000000000..826e0d0ba888 --- /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 469/846] 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 f7db2563d2b1..df06031d999e 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 470/846] 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 836897927b01..c7a3099b8ab0 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 e959c1a65112..cc7d3a04f003 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 471/846] 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 28fd55f6ff0a..1d238b242b05 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 bb853d237047..7caad34edb3a 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 8582080498f3..d817c08b52fe 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 ce942751ada8..8173944d9591 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 eb786bb39f95..5a40cf381d61 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 472/846] 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 1d238b242b05..50d9c93f9d40 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 7caad34edb3a..9f1948359e7d 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 d817c08b52fe..ad9953d12bcc 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 8173944d9591..1040fee4b34d 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 473/846] 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 11bdd27d9b1b..4b6a0a6a0c93 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 bf58c117aaaa..c50c6b900ae0 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 cbc0a41036be..5f26ef413e23 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 474/846] 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 4797771e7bdb..c9d18c3cb728 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 4d284ae1319d..163bd044c178 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 2d08e82f349a..8c849e3209b0 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 a2ed5392bfc7..03d585b39750 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 475/846] 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 7e9190bef5e7..3f5b3a5bcd5d 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 2992abae775b..2e2bd054e42a 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 56f6a855dfa5..2a0cc1f18e27 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 bb58483584b3..4cbbc8b8cd43 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 476/846] 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 2498c48f067e..22942d9fb0c9 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 076692e64451..07d7f0b45b0c 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 3bf5f715f833..6ae059c9425d 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 a106582a6332..62f282324752 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 477/846] 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 0dca6a5da0c0..7f4f16f8faf9 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 478/846] 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 99652ca4470c..f44305d7e483 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 0d0eb19cd072..ef3ad73ebc7c 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 479/846] 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 7948d99162b8..92f42168a1ea 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 316426f1cf18..e7e0a31febc4 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 be0914e4a794..ccc3cdb2b742 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 480/846] 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 e7eb7c2e9802..717741129a8f 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 414477aedd78..d75cdd625f9e 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 3ad1a29aebad..1f261159f4a9 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 481/846] 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 67423e6ec1d1..287f8935327c 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 fab43b738eb4..351988f31ead 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 482/846] 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 717741129a8f..5683a71efea4 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 483/846] 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 3c5541e64b4d..a6e4252a0c82 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 09614b8c1ad7..73e550b3df89 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 eacf948b3927..fe878e5f2060 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 484/846] 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 5fa8009a8b42..99e371631b14 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 1e032a7bc20c..d951ca0e6308 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 485/846] 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 5f7b1e85ee9a..631064cd92c0 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 486/846] 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 77c206002ea7..c0890018d46a 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 000000000000..5caf29c61973 --- /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 487/846] 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 031d69e86a13..f1df634701dd 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 fef9f4f39f80..310769cd002b 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 dc738064a171..1e0b3e4d20a5 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 488/846] Apply suggestions from PR review --- CONTRIBUTING.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 631064cd92c0..54777810abbd 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 489/846] 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 8aa478ea2d69..d8fad217e5a4 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 e29a1dce0c7e..1f4864b72895 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 490/846] 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 d8fad217e5a4..49cb2ffc4e37 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 79f276634619..2735e41738f0 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 644161d9f5d8..afd704ef12a9 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 491/846] 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 310769cd002b..a414832bcd36 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 492/846] 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 22942d9fb0c9..4f0a533592c3 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 2c9d4d39e6c7..e785ac02feb3 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 113fab690095..5ffa6c4fd061 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 493/846] 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 5ce63c0a1574..a5da0f7b7675 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 000000000000..c8e53ce6f952 --- /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 d45394ab8d21..f78de7a175f6 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 c50c6b900ae0..eadd2621a401 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 000000000000..d40fa00c3155 --- /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 000000000000..74b9078afe8b --- /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 494/846] 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 22c49a20451f..99e4b293ac68 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 2aef995cec44..25301d6dede0 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 b73012a4691b..780389f32bc1 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 495/846] 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 3aff8741f605..ee3ce26ee405 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 496/846] 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 ee3ce26ee405..d529952bc69b 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 497/846] 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 2bad3b9c57f0..e70da43ab47a 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 498/846] 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 c8e53ce6f952..dc1c3bfc9ff3 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 b6a1e0a6aa99..d44854aefe97 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 499/846] Fix typo --- tests/fmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fmt.rs b/tests/fmt.rs index d529952bc69b..7616d8001e88 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 500/846] 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 e5daa30f8ca1..b691d363d2f2 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 d81676a3d9f4..1f3b8ac99b19 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 ecdba921a5d0..dc21de0001b6 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 eee3db9b7d4b..797817662427 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 be2e0f38fd16..a0eb51cc9760 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 acb85f7100a3..d0defb5a79ed 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 501/846] 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 4f0a533592c3..1ef54d285f68 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 502/846] 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 25301d6dede0..e6be5c4588f0 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 503/846] 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 dc1c3bfc9ff3..9ac5a45eb459 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 74b9078afe8b..b34c8159da04 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 504/846] 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 5f88dcb188a0..e653240d0491 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 505/846] 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 a5da0f7b7675..137b561028a6 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 f78de7a175f6..577ce6523b49 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 000000000000..1c7e62ecd3d2 --- /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 eadd2621a401..687fac7baa84 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 000000000000..2aa842adc856 --- /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 000000000000..56bc2f2d1c00 --- /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 506/846] 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 137b561028a6..34d488210235 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 507/846] 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 4b6a0a6a0c93..006d7a3a12d9 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 3b46324704e1..eb8105c6b6da 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 508/846] 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 1ef54d285f68..8e91cbb3cdfe 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 2c2d1e275893..e98360ea6911 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 dd5a24bcbe7a..8217bc6187f9 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 509/846] 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 8e91cbb3cdfe..2388310628f6 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 e98360ea6911..e82873629a54 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 510/846] 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 2388310628f6..63e0c183113e 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 e6be5c4588f0..07ec59f452a7 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 511/846] 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 3f5b3a5bcd5d..16e48d919164 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 2aef995cec44..d20b33c4a1d1 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 2e2bd054e42a..fec115ff29d6 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 2a0cc1f18e27..90fee3aab23b 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 4cbbc8b8cd43..456b12a2c6b1 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 512/846] 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 137b561028a6..7af3b666cca0 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 577ce6523b49..b70d126af5bf 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 000000000000..3a71a0db6fe2 --- /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 687fac7baa84..ad57146048ea 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 000000000000..21e9efca87bf --- /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 000000000000..74273bd9abb2 --- /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 513/846] 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 3a71a0db6fe2..4e08c991bd02 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 514/846] 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 4e08c991bd02..239b4bdbdb1d 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 515/846] 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 ad57146048ea..b4f20ca7f14d 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 516/846] 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 239b4bdbdb1d..2901f393fc65 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 b4f20ca7f14d..1f56c56f081d 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 21e9efca87bf..056778995a4c 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 74273bd9abb2..3b9ac69f20dd 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 517/846] 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 07ec59f452a7..d9598c4abbde 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 518/846] 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 d9598c4abbde..820052571156 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 519/846] 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 63e0c183113e..9996df69470f 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 520/846] 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 137b561028a6..b37273af44d2 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 000000000000..229536bd6730 --- /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 577ce6523b49..7943be34c623 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 687fac7baa84..e51345109226 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 000000000000..50f31f0c9c54 --- /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 000000000000..00ef37f413f7 --- /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 000000000000..3ae4680d9296 --- /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 521/846] 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 229536bd6730..7ba6facda6ae 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 d44854aefe97..65320d6a0e0b 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 522/846] 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 7ba6facda6ae..4fede8857c70 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 523/846] 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 4fede8857c70..bf47c1f84a27 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 524/846] 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 820052571156..fe2ee0931573 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 67faa8bd4a0a..5fb568672d35 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 9867e2eedcff..737b0f7e55bc 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 bc5978b538f1..b8a436993f32 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 525/846] 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 bf47c1f84a27..1042eb455246 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 526/846] 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 34d488210235..99a8b1a6293c 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 000000000000..ae347fcd3e8e --- /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 577ce6523b49..0eb1d3313660 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 687fac7baa84..dff19ef440f3 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 000000000000..cadc6494c764 --- /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 000000000000..898fe1a95613 --- /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 000000000000..112984cdccb6 --- /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 527/846] 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 cadc6494c764..9b1a7ac3ba9d 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 898fe1a95613..731c094edb42 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 112984cdccb6..17d0c3751064 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 528/846] 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 ae347fcd3e8e..88d9d3b5a263 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 529/846] 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 2901f393fc65..11fefc12316b 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 056778995a4c..f6fb2f1ab612 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 3b9ac69f20dd..9faedf829860 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 530/846] 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 11fefc12316b..8ba365cb00c3 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 531/846] 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 9996df69470f..7c4a78cbdcdb 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 532/846] 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 1042eb455246..f80a0efa7a55 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 50f31f0c9c54..0e28f87e33d4 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 00ef37f413f7..1f226298c0d0 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 3ae4680d9296..0c97bdd0f7ab 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 533/846] 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 2955f8d8e591..811fde388d15 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 561283fc8e73..efd4187947b2 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 dc9d9b451c0f..657f32d38a32 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 f56dd65b9dd2..acd1ec3f2cae 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 534/846] 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 067ea903bdd9..0b0a13078768 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 000000000000..d05567a3f824 --- /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 2f1490a70369..447e70c0bbbe 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 26b2057548bd..df8a5b94ddcf 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 535/846] 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 8ba365cb00c3..8b8e211cb723 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 536/846] 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 50d9c93f9d40..789d124eae27 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 0d8a322f2b2b..26b03bdc7405 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 537/846] 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 cfcc1b3c5f35..c8f153e7201c 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 b222e2f7976d..a5fcde768f18 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 3422eace4ab9..0396d39e3d54 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 57ba976730c1..d0194e4bbbe5 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 538/846] 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 292dbd7ad6b4..9c5a12ea9c8e 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 539/846] 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 c95e43a94304..25345f8fa316 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 bfe27e020445..0903a8738265 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 540/846] 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 0eb1d3313660..8e80779377bc 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 000000000000..c63c201a9f35 --- /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 541/846] 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 c63c201a9f35..43bfcf0b8f14 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 9181d789d4fb..6b5bcdaa78e2 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 35ae88b78a4c..7974bf56d44c 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 000000000000..f3a74ad95cd2 --- /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 000000000000..8cd37d8c0252 --- /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 542/846] 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 99a8b1a6293c..dfe45a46f092 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 8e80779377bc..3794cae091a4 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 dff19ef440f3..6725c97f7938 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 543/846] 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 3c3f8b26e3ac..fa8dd210ebad 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 544/846] 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 77620c857e66..68c5d32846f1 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 ae9bb394cb9a..23fca43590b4 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 545/846] 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 16e48d919164..e83d491fac3b 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 4ce4cdeefb48..f394b980127c 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 546/846] 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 43bfcf0b8f14..9211113ed046 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 8cd37d8c0252..7a269ab95ab2 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 547/846] 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 cb0d95f5e264..8f2922b022ab 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 548/846] 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 0b0a13078768..320a2a257bd0 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 549/846] 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 320a2a257bd0..3048436d9a7b 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 550/846] 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 96e9d0f32f8e..bea0a4d24593 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 551/846] 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 9211113ed046..649de4133e06 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 552/846] 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 89fde1d509d3..50b39cf4ea7c 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 553/846] 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 c640c82d6d7c..dc6c8ba2f154 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 554/846] 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 fb12257021a1..a27ce945ca58 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 555/846] 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 25345f8fa316..b65ae15edcd9 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 0903a8738265..0928820892b1 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 556/846] 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 4df6827d77f9..ab6e34d201c6 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 d44854aefe97..9837759fd5ef 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 913cd004f19f..bc725a645acf 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 d3a360ed8b57..b1943bf9d703 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 557/846] 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 8b00d29acb52..9b6a9075a295 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 31c2ba0f9c58..ad0d0387db03 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 a3c8ae468ede..9746f6e6849d 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 558/846] 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 f13e369907b2..f417e3a0caf9 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 0928820892b1..bd4792c4a76b 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 ddc5d48cd413..4896479791af 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 559/846] 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 f417e3a0caf9..c27acdd22365 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 560/846] 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 c27acdd22365..c59185bd7bd1 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 561/846] 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 ab6e34d201c6..1d17fbd37254 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 000000000000..fd502cba73a7 --- /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 000000000000..02b86397ed5e --- /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 b1943bf9d703..d3a360ed8b57 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 562/846] 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 bd4792c4a76b..7ca829854db1 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 563/846] 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 7ca829854db1..a37c8782ec33 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 4896479791af..d9ffa15780ad 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 564/846] 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 c59185bd7bd1..6c54c07869ad 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 565/846] 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 f80a0efa7a55..4002fb655a5e 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 0e28f87e33d4..8ed53a56ac04 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 1f226298c0d0..19c8fc24ba23 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 0c97bdd0f7ab..67298fc47095 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 566/846] 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 649de4133e06..fa59b6ec3706 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 7a269ab95ab2..e4e6a289ba03 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 567/846] 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 3794cae091a4..797ab62cb544 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 fa59b6ec3706..5298e16a04d9 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 6725c97f7938..5105e9531626 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 f3a74ad95cd2..617b64228726 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 e4e6a289ba03..7273f4603807 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 568/846] 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 7423107e8f94..fa3a99dad9d2 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 569/846] 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 bc725a645acf..548c5929c612 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 d3a360ed8b57..ac38ab8effd2 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 570/846] 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 f2bd651ab253..c67efc10f157 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 571/846] 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 c82deaa43b26..550ae2927a82 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 572/846] 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 1f9ae8c931a1..076ef235b8bd 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 1312c70b6d59..d7d45ef9b0b3 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 467e00263cd3..4f3a8c6b7923 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 b23cc608621d..88e4efdb0f05 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 573/846] 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 7af3b666cca0..d4f0ff4ba78d 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 b70d126af5bf..dbe17e6bbfe0 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 8b8e211cb723..4077aba6ef17 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 1f56c56f081d..4d6c45761e66 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 f6fb2f1ab612..287726f7a2d4 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 9faedf829860..c6936fd86923 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 574/846] cargo dev update-lints --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4f0ff4ba78d..6b6be50065cd 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 575/846] 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 2d964ac2b9f6..748f45f47c2a 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 000000000000..2f60a98f037b --- /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 000000000000..26ec15be518c --- /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 576/846] 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 748f45f47c2a..ff2c281ec9d7 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 dff19ef440f3..5dead1b9b691 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 000000000000..550bdb001e4c --- /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 000000000000..362e104a2448 --- /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 577/846] 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 2f60a98f037b..1ddc12f9ab21 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 26ec15be518c..c09d2d6ab21a 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 000000000000..590699025072 --- /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 000000000000..7210431bd1bd --- /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 578/846] 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 ff2c281ec9d7..d4e070b0c24b 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 579/846] 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 d4e070b0c24b..614e33a06da3 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 580/846] 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 c09d2d6ab21a..4b9bfef79748 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 7210431bd1bd..de7026f3ffaf 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 362e104a2448..694548ece97b 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 581/846] 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 6c6188d61ad5..b6d405cca770 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 582/846] 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 2b6ddadd4c11..fb891866364c 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 d6f56d40c5d4..ac4c1bc65979 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 4cc32c79f05c..7d79901a28d1 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 583/846] 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 f18e7e5d9975..367534499fd0 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 584/846] 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 fa3a99dad9d2..0826ad0ab55b 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 585/846] 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 8352a8a3d2c6..f417e3a0caf9 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 ff1088f86f64..bd4792c4a76b 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 ddc5d48cd413..4896479791af 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 586/846] 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 f417e3a0caf9..c27acdd22365 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 587/846] 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 c27acdd22365..c59185bd7bd1 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 588/846] 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 bd4792c4a76b..7ca829854db1 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 589/846] 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 7ca829854db1..a37c8782ec33 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 4896479791af..d9ffa15780ad 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 590/846] 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 c59185bd7bd1..6c54c07869ad 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 591/846] 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 614e33a06da3..703d8a6f62bb 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 592/846] 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 ba69c8266b11..98e027b6d229 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 1cd6400b0195..28003b365bbd 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 593/846] 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 615440e15f38..4e4a206a583a 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 f1e880d2696c..11c6efb25cce 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 b765727c168f..2e0d9129bfb3 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 594/846] 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 81a34021e8a0..a4ee54076ee9 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 1795fd10fa13..c017c5cb5d02 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 04d486438b1e..a7d38c93433d 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 1cef8c2cfc99..fbdc977b769a 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 320d9cc3f643..798551118309 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 595/846] 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 285a2ff8060d..a6fafdf53572 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 c017c5cb5d02..38ddc69c8cbb 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 000000000000..127938aecd63 --- /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 65320d6a0e0b..f0f7719e2fdf 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 a7d38c93433d..8bceef80abff 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 000000000000..d1b4772c7dee --- /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 000000000000..1352a8713d4f --- /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 596/846] 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 50121a054c79..62bb70af06e9 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 6c54c07869ad..8f5675a61b90 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 02789735c17a..9cb1cfb915d5 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 57a45e628db6..1a7f36fbdadb 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 802beeb4be6b..32a67f181df4 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 c53a63a541bc..7de560c73486 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 597/846] 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 285a2ff8060d..8922f5e70279 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 c017c5cb5d02..239eeb10bb42 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 b6d405cca770..5f3a2e0b6d42 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 65320d6a0e0b..2df11d2efcfb 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 a7d38c93433d..d0c6a1d63d97 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 000000000000..c8c2bec67ee5 --- /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 000000000000..641a13a22513 --- /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 598/846] 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 5f3a2e0b6d42..da04d07885b9 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 000000000000..a878b0ab3365 --- /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 000000000000..c4b016210469 --- /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 599/846] 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 d4a50dd9013f..81e6214f143e 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 2d454e8e70de..f7c380fc915c 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 19dc4a284b72..5d0455363e8e 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 600/846] 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 087d50c90e67..ef4a54735a49 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 601/846] 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 44afd7e82fe7..b60a80e07d70 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 8fa5d22210a3..5beb4be5b292 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 3ebbfed64562..8db7e693e626 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 000000000000..fe950b0aa7c7 --- /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 000000000000..c89137eb758f --- /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 602/846] 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 6c54c07869ad..3d2fd0eee85a 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 98e027b6d229..de966cccd111 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 9494efe736cc..60e5e7bfed39 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 603/846] 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 e4e65b5f4d42..0a58231558ed 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 9dd4c8a5f7a7..53c3d084dbc9 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 604/846] 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 5beb4be5b292..f201494a0246 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 c89137eb758f..5e5fe9e3a3e7 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 605/846] 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 ef4a54735a49..225fe58906f7 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 606/846] 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 81e6214f143e..67a3685fd0dc 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 607/846] 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 d1b4772c7dee..cbb84eb5c7e3 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 608/846] 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 127938aecd63..4afb0ab3badb 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 609/846] 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 498f12518f8a..ae37942e55a1 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 98e027b6d229..914f9f94e776 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 31517659c34d..08b3eab9b7cd 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 9494efe736cc..5e2652b48cb2 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 000000000000..6938d9971d96 --- /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 3ebbfed64562..7efeef626904 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 811fde388d15..ec8b7e59b597 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 4a64b935ac9b..ea1dc3be29ba 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 695a460cc4ed..a7fb00a27057 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 dee80d26bd97..895fd86321fa 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 7005850efaf8..b69fe7676827 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 fa66e68794e4..4980c1114999 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 04f47d1aa297..0b270939ec20 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 5c1b2eb1f14e..1cf7ac46346d 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 610/846] 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 c8c2bec67ee5..1fa986439368 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 641a13a22513..e4cc169af07b 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 a878b0ab3365..5d586584817b 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 c4b016210469..8252270d2ac7 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 000000000000..5d31a848cf72 --- /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 000000000000..e69de29bb2d1 From ad6f8c6354f4f1bba8101886646929a4985dec77 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 16 Sep 2020 12:09:57 +0700 Subject: [PATCH 611/846] 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 cc7d3a04f003..341d9e601ee6 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 612/846] 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 411229935c36..ed3c83af616d 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 613/846] 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 54777810abbd..100c9edb3672 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 3c782e9b17ff..21e0f6f4fc76 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 614/846] {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 e653240d0491..fac63bcb9937 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 3f710540e903..a43a1fc4f524 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 05fe88915d6e..54b3ad75b31e 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 93afd73d1114..1c1b1b58402e 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 2473329ca727..a14e86122ee5 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 615/846] 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 4980c1114999..4ba2a0a5dbcc 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 0b270939ec20..466915217e42 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 1cf7ac46346d..44dcd0cafbb6 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 616/846] 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 a28eda8be15a..741195f3b10d 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 000d5269930b..ca8ca53c80c3 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 2b3f9be2dfb9..2f6c9e2f4e5a 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 ec6c157ac1a2..2231deee833e 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 617/846] 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 189c07427ae9..d7d02ebf985c 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 8ece44878fe3..c0529a34cc41 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 d616502a82a0..35a5d00f4aa5 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 000762334f61..a9e05fddbe76 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 8bd85af87682..d6541010bca2 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 f330fa8fab8f..0877b44d9013 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 42a98dc963d2..c9c4891bb08a 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 57966452253d..331b6c6c34a9 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 7ba7397c29cb..11380f83316f 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 184aee95efa7..dadd0f8ebb7c 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 67a3685fd0dc..909e79f661a6 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 0826ad0ab55b..8a2dbdc50eae 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 e99d0317ba2e..73a99a3a2f87 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 b8583402928b..6eeb031d383c 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 c0890018d46a..ae6013530091 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 7a659bf779c0..15b66684eab7 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 47a73ca9a24c..54b38d9f4ced 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 1c7e62ecd3d2..0f8797243eca 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 f201494a0246..bfe426a25eb8 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 95eedf881784..ea52741b7cc4 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 618/846] 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 239eeb10bb42..06f41be8a2a9 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 da04d07885b9..a29a199b8c3a 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 d0c6a1d63d97..e5563151b48e 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 619/846] 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 73eabd4207e7..28c68a2b68c5 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 39f875105485..9fcc9ece49bb 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 5800af7e960f..ed726a6b46e6 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 b4003ed8932d..7471b3605406 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 6a9a57361f9f..0fcb726db46e 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 620/846] 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 28c68a2b68c5..bb44eeb6adc5 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 7471b3605406..646d3ec8b472 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 0fcb726db46e..0a0b818b8b7f 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 621/846] 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 646d3ec8b472..3afcdca2f04d 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 0a0b818b8b7f..5cb10be88d89 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 622/846] 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 8084fdefdc23..17d908336d59 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 48a32cb1c7b7..d57fbb14ae49 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 17185217e895..955900f3e6c9 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 000000000000..499b975b2bb4 --- /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 000000000000..2a98435e7902 --- /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 000000000000..eebb34484913 --- /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 623/846] 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 6f47687c4108..b1a4e06d4c32 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 e0b58157590a..000000000000 --- 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 53799b4fbf1d..000000000000 --- 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 17d908336d59..fe8f62503b76 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 d57fbb14ae49..09426a6e5908 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 955900f3e6c9..3473ceea00e2 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 624/846] 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 3410341a1e3c..7f998c63f497 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 aa6ef162fe40..81d8221bd13e 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 625/846] 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 ea52741b7cc4..3a005e2513ea 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 5fb568672d35..67faa8bd4a0a 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 737b0f7e55bc..9867e2eedcff 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 b8a436993f32..bc5978b538f1 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 626/846] 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 3a005e2513ea..92cb31fcf854 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 67faa8bd4a0a..2045ffdb5f09 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 9867e2eedcff..522f31b72d01 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 627/846] 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 3ee022e4e68a..0000d39263ed 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 628/846] 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 9b6a9075a295..1307237dbc70 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 ad0d0387db03..b45b27d8f23b 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 9746f6e6849d..be2abe7f7014 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 70c6cf0a3b63..50607933e18f 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 629/846] 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 fac63bcb9937..780d474ee969 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 000000000000..2d43d452a4fa --- /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 000000000000..e69de29bb2d1 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 630/846] 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 7f998c63f497..9f3be26e6723 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 631/846] 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 9f3be26e6723..a59d3ef8708b 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 632/846] 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 a59d3ef8708b..20e75546d710 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 633/846] 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 20e75546d710..61f0059cca47 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 634/846] 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 61f0059cca47..2b7830e7cadb 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 635/846] 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 2b7830e7cadb..bf067c70a7ec 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 92cb31fcf854..a0ddcce111e1 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 636/846] 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 bf067c70a7ec..1c316c00f433 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 637/846] 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 1c316c00f433..d9896f7fd6b1 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 638/846] 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 d9896f7fd6b1..157dff59d449 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 639/846] 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 157dff59d449..c036d63c3351 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 640/846] 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 c036d63c3351..bd2ae47b7234 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 ec8b7e59b597..50d48650a093 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 641/846] 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 bd2ae47b7234..2aee3b93e825 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 642/846] 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 2aee3b93e825..98c411f5ae60 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 643/846] 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 0083f94798fe..87bfb4fdd163 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 bad84a589008..a5698b08103b 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 644/846] 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 a5698b08103b..e6bef9981a3d 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 645/846] 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 87bfb4fdd163..4846ab5eaaa3 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 e6bef9981a3d..d189bde0508e 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 646/846] 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 50d48650a093..aada4122e78a 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 647/846] 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 aada4122e78a..8f2aa724d20b 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 648/846] 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 4846ab5eaaa3..8318fd898112 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 649/846] 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 98c411f5ae60..5928d12f30be 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 8f2aa724d20b..1f448f86d72e 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 650/846] 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 5928d12f30be..f684e7de8f83 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 651/846] 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 1f448f86d72e..062b273c0f40 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 652/846] 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 f684e7de8f83..4ff567ffb0ec 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 653/846] 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 4bb833795bb1..948deba3ea6e 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 c48c7e9e6c6b..98588be9cf82 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 db1fa871afe0..dca32aa3b561 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 15d0f705b367..b0a3d11bce46 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 1ca6b6976b38..81af88962a64 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 837ec9df31ab..96a8fe6c24d5 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 ffef1631775e..45b3b1869dde 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 ac19f1976e91..3f3986b6fc69 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 d832c2860332..56024abc8f58 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 9e5e299c336a..f1a229f3f4fa 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 47f8e3b18eea..d44b0fae8200 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 47324ce18316..8326e3663b05 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 c4f6c0fed682..6b1ceb505693 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 ffad2d06b56e..fef4d7db84dd 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 95c7dff9be36..7d62e315da2f 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 a548415da62b..1253ddcfaeb3 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 d0b44ebafeeb..cdf018cbc88d 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 cdb59caec67e..2a4bc83f58a5 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 ead67d5ed1b1..08b06961760f 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 b06df83d51a5..0cbceedbd6bd 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 e02eb28ab865..30e4b11ec0bd 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 4ef992b05e76..2f913292995e 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 3d4a88ab3c4e..05696e3d7d56 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 c4acd5cda1b0..bb238c81ebc0 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 848f0ea52ca5..94c939665e61 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 d8fbaa541466..a238e7896fc6 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 48507efe1e98..4f61c76828db 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 bd1fa4a0b1ef..676564b2445d 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 f79d9ab6460b..c7468493380c 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 3d5063d1a3a7..a41bcb33b446 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 f2153efc3880..8021ed4607dd 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 3a4bbe310cca..1ba7ac082132 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 2bb95c18a391..60105a8213fe 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 265017c51d92..6d2124c12fe9 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 654/846] 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 697823712bf0..f0d73e9b0e2a 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 655/846] 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 e6626d57a772..01796d45f138 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 05ffb55f6207..3df8be6c2323 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 086548e58ed6..619d11cefc46 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 656/846] 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 780d474ee969..0e9c7098af89 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 2d43d452a4fa..b84bf9124fc8 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 657/846] 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 d1dfe36ffd82..575cbd60792f 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 000000000000..7088b2718f20 --- /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 58112ac8da5f..7c886ab87d0e 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 9c5a12ea9c8e..07591ce22298 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 9603023ed067..a77bbcb0abe8 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 000000000000..a1f515e443dc --- /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 000000000000..3d3f0729abd8 --- /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 000000000000..5da551cb430b --- /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 000000000000..a54a04b4d2c6 --- /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 000000000000..93dabf38cfcc --- /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 658/846] 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 7088b2718f20..5ecdcc0e08aa 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 659/846] 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 a2984d736416..62a8be0abf22 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 a77bbcb0abe8..57dc48c0667b 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 660/846] 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 5ecdcc0e08aa..42fbff8ff879 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 a54a04b4d2c6..46c9185268c6 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 661/846] 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 6fbba01416a8..103ec27e7d75 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 662/846] 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 42fbff8ff879..7638019340f0 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 7c886ab87d0e..7b6efc660af6 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 663/846] 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 7638019340f0..eea369924a6c 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 664/846] 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 eea369924a6c..603f776e6888 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 5da551cb430b..ed91b5a6796d 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 93dabf38cfcc..40db1b946d88 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 665/846] 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 603f776e6888..581c3242e374 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 07591ce22298..03f8c5a2c075 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 46c9185268c6..000000000000 --- 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 40db1b946d88..000000000000 --- 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 666/846] 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 57dc48c0667b..76e655ad6030 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 667/846] 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 0e9c7098af89..d9d60fffcd7a 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 b84bf9124fc8..997ebef8a699 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 e69de29bb2d1..000000000000 From 71b6d54cd911bdbbc8564dfb17e991cfa1e9a9a8 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 26 Sep 2020 23:54:18 +0900 Subject: [PATCH 668/846] 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 000000000000..3cc957652104 --- /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 669/846] 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 3cc957652104..3507168a3a96 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 670/846] 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 a29a199b8c3a..17d950169fd3 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 671/846] 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 4ff567ffb0ec..647537933f72 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 672/846] 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 647537933f72..f2df53aee4f6 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 8318fd898112..84758275dd74 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 d189bde0508e..464b18984fbc 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 673/846] 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 f2df53aee4f6..7c8b6f483bcb 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 464b18984fbc..db62ed90d97d 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 674/846] 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 e5f7cc511112..3e786da28dfe 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 f59ed6df1a99..dfe2aadffc04 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 6809b1fa88d3..c1684575930d 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 675/846] 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 c1684575930d..3773b9d9a2ee 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 676/846] 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 7c8b6f483bcb..215700fed8cd 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 062b273c0f40..0b2cb667bf41 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 677/846] 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 000000000000..a49ba9eb10af --- /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 000000000000..24393ad9b4d4 --- /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 84758275dd74..0083f94798fe 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 db62ed90d97d..54b966f6e541 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 678/846] 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 38bdd0f7ed23..7687962bdd9b 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 01796d45f138..de670cdfc31f 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 000000000000..a39d96109f17 --- /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 000000000000..44a0e6ddeace --- /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 679/846] 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 fd502cba73a7..0c2d48f938fc 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 02b86397ed5e..d68bbe788021 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 680/846] 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 1d17fbd37254..530968c191f9 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 681/846] 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 530968c191f9..d7043e7bd8f7 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 548c5929c612..d482d466e449 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 ac38ab8effd2..c8a2e8b81c01 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 682/846] 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 575cbd60792f..0de6f4b4235f 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 000000000000..ef1f1a14afca --- /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 529c2450541d..10da59c7a7a0 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 76e655ad6030..16ceb6179654 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 000000000000..049bfa604d50 --- /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 000000000000..27b51166eacb --- /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 683/846] 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 049bfa604d50..658cae397e14 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 684/846] 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 6c54c07869ad..294f0449281a 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 685/846] 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 7f998c63f497..61b63597b163 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 a82b11591619..7633316e0f87 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 686/846] 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 215700fed8cd..9a1ba4907cda 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 687/846] 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 9a1ba4907cda..2ae5a9acb75c 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 688/846] 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 5e8800d38eb5..52d3c2c1daf0 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 b75f10917df1..dd683e7f746a 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 79ce38be19bd..d5d27c853c28 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 689/846] 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 2ae5a9acb75c..ea40c2af4f71 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 a49ba9eb10af..70873c9e9944 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 24393ad9b4d4..598c881b4d6c 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 690/846] 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 ea40c2af4f71..4f279cc5ef7b 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 691/846] 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 4f279cc5ef7b..d3a1683cc0cb 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 692/846] 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 d8c57f0e7ae7..d3fe60042a87 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 27db9594f3b3..f3fd1c57da63 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 4575a132e5b2..b0445b070fdd 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 693/846] 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 16ceb6179654..3654dbc6124a 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 694/846] 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 70873c9e9944..ba388a05a285 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 598c881b4d6c..2547b19f5d1d 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 695/846] 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 d3a1683cc0cb..3e9265c12153 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 696/846] 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 62bb70af06e9..07f604cf7147 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 697/846] 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 10da59c7a7a0..71bd7c4f82a4 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 bb44eeb6adc5..7b662eae7753 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 16ceb6179654..ccdfac3a34b4 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 698/846] 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 0de6f4b4235f..617bf32f4639 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 10da59c7a7a0..91244ec2724b 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 d3fe60042a87..d6c8d317dc2f 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 3654dbc6124a..e7df733d3a27 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 f3fd1c57da63..b6944e048593 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 b0445b070fdd..595d80ea2792 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 699/846] 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 d6c8d317dc2f..93d59cc7fcd1 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 b6944e048593..1f596c312fe3 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 595d80ea2792..3fca463c620b 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 78695bd4968d45ef6afd3433af4bf6f310128770 Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu@web.de> Date: Mon, 5 Oct 2020 12:08:57 +0200 Subject: [PATCH 700/846] 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 a36fdca5d5de..c54103b25c20 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 baee77357303..8275bfa5db71 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 6de447f40214..8a451807aeb4 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 48a7ae904948..f4c5adfe82c2 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 701/846] 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 8275bfa5db71..70cdb067d913 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 8a451807aeb4..729990af3998 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 f4c5adfe82c2..b338b8aa6228 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 702/846] 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 617bf32f4639..a6631e6d5469 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 703/846] 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 c8f153e7201c..f6eadbdef0bb 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 704/846] Remove "thanks" section --- CHANGELOG.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6631e6d5469..0bd13320dc95 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 705/846] 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 c861efc8afb5..d745000eac7c 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 5baa31d5cde0..567831354f58 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 706/846] 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 3c385343f701..a8f3e3145f64 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 f0d73e9b0e2a..0e8f7683103d 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 81af3d3033b2..48e0478f1699 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 707/846] 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 341d9e601ee6..fcf817b82c89 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 72ad8d12e6d7..826a059f92ab 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 065c7c042d36..b4b4b3dc18d5 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 03f8c5a2c075..dd2fd0bb445f 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 708/846] 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 dadd0f8ebb7c..a9650e531b67 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 709/846] 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 d2a322e1223c..71a30d1c33d4 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 e0651f9ab5d6..ec70b2f1e203 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 247effde19b9..b2a8e3f08d5e 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 710/846] 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 32685038067d..8df6e19421ec 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 4324984dd60e..df4e9e20b649 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 000000000000..d055f17526b9 --- /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 000000000000..537557f8b0af --- /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 711/846] 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 72ad8d12e6d7..6ae8ae9e38b4 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 15b66684eab7..203af06a7346 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 0dba5a71c502..959ac6d42bfc 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 712/846] 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 72ad8d12e6d7..a5a245367720 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 17d950169fd3..5e83b6c81ec8 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 0dba5a71c502..cc66b1a2db66 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 713/846] 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 826a059f92ab..d11eeff80323 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 000000000000..a05cb6270b76 --- /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 000000000000..209081e6e801 --- /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 000000000000..69162870807a --- /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 000000000000..45d8c60382b5 --- /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 714/846] 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 617bf32f4639..552a0e96ceb4 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 0dba5a71c502..3cba9867600d 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 715/846] 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 53c3d084dbc9..27d4f1b03d77 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 716/846] 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 27d4f1b03d77..d56079a4ab73 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 717/846] 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 a05cb6270b76..3be792ce5e4f 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 718/846] 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 dfc158661cbf..95594e38c9ec 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 9767e5bf76a8..f7f3b195ccc1 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 719/846] 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 82549c12d0a2..ffe81b3966c7 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 720/846] 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 ffe81b3966c7..b222475486d9 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 c0a52d832c00..377a0fb502f4 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 721/846] 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 203af06a7346..415c07b22614 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 722/846] 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 415c07b22614..3783bd78de25 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 723/846] 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 b222475486d9..8b0229125738 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 724/846] 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 377a0fb502f4..3d5a02bf705c 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 725/846] 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 3d5a02bf705c..7d3f4011e999 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 726/846] 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 d745000eac7c..b8a4a20114bb 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 567831354f58..43cb2954b74b 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 281037ae37c9..7a8cbd5251da 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 000000000000..a46c0e4d3f0a --- /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 21e0f6f4fc76..2869c3bf7d44 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 727/846] 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 c81e7f6e0692..38959e2331b4 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 728/846] 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 7d3f4011e999..07004489610d 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 729/846] 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 99e371631b14..0cbe73affda3 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 fd0cd7a1890b..a5c00b3418e5 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 ec3b43c2f43b..5ee157cf23b8 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 f542f9b02c17..5b7252532c21 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 cc175e8bf247..4f25a86b2e4d 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 730/846] 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 391952ea6b14..eaa6a9af277d 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 731/846] 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 4a3eb9c983a1..383a62da821e 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 732/846] 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 0cbe73affda3..cf4aa39e49b9 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 a5c00b3418e5..f83861950f8c 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 5b7252532c21..15aeaf907dc6 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 733/846] 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 f83861950f8c..7509d90c6c2f 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 734/846] 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 259696658eab..15f3d1f08060 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 735/846] 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 d6541010bca2..26da058598e4 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 306514769990..740a22a07d74 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 9734492154e8..96df7f37f779 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 736/846] 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 062c9bd2d9e6..c5e33b288a9c 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 9fa0e5f5c079..586784b73e69 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 f7c380fc915c..bb4051c46620 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 737/846] 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 000000000000..f3966e47f5e8 --- /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 738/846] (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 5e83b6c81ec8..a982b92bb2b3 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 739/846] 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 8a2dbdc50eae..4525b12689fa 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 a982b92bb2b3..9a948af8bfc4 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 740/846] 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 000000000000..9f73f39f10d7 --- /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 741/846] 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 2869c3bf7d44..344bb455aa53 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 50d42678734e..2ab51168bcaa 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 50d42678734e..2ab51168bcaa 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 2c13c327d798..e16ed600ef81 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 742/846] 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 6fb6cf75d96f..f21768c44988 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 50b39cf4ea7c..212a31006370 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 097eca0af578..26a727687b1d 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 96c9d12d75fb..d0fc8f0c8a90 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 445fc8d31d77..f47b81a450ea 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 f44d6693d303..c7b616e28970 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 a48e675e4ea2..05e087b08bc1 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 bc37785294fc..8290ece1cad1 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 000000000000..a66f581b2159 --- /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 000000000000..986d9718acdb --- /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 743/846] 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 212a31006370..fd45a6da61ca 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 986d9718acdb..b8230032491b 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 744/846] 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 a9a709299426..556b67e0b374 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 377f6d224463..c9b07855af10 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 d0fc8f0c8a90..624223ff7062 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 745/846] 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 c7a3099b8ab0..13db35f4b0ef 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 c9b07855af10..e32ba116939b 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 746/846] fmt --- src/driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index e32ba116939b..e5d740cecd31 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 747/846] 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 e16ec783fab7..7126c98a0b43 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 fc4afde9d9e6..dd99b6b9040c 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 3df8be6c2323..e369f62f8bfe 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 9c7590c7dd63..ff1dc76ab63b 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 000000000000..cf5660ea98da --- /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 000000000000..9206e9026e95 --- /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 8e0243c49aaa..d8bda7e8f48a 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 748/846] 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 38959e2331b4..f25edb793e26 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 749/846] 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 7126c98a0b43..a95d71042ee2 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 cf5660ea98da..25e1c6ac6b75 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 9206e9026e95..1df094fae180 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 750/846] 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 10a64769585e..6c969c3ead0f 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 686867cf5c6f..7f28f0257904 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 751/846] 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 a95d71042ee2..9653e62cad07 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 dd99b6b9040c..fc4afde9d9e6 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 272b0900a31c..3ab4dfc439bc 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 5b80e6078eed..21a63aec7a17 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 25e1c6ac6b75..000000000000 --- 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 1df094fae180..000000000000 --- 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 752/846] 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 c75adb62f257..47c650ac27d4 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 753/846] 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 62a8be0abf22..e1b3c84d6917 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 754/846] 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 3ab4dfc439bc..20613ac6afe9 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 755/846] 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 f21768c44988..93ce6bb85d8a 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 000000000000..097aff4b1786 --- /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 26a727687b1d..2e9900815d9a 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 383a62da821e..eb7624b25a3c 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 6938d9971d96..30e812c284b0 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 ea1dc3be29ba..2fd6046ebcf5 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 624223ff7062..6dc95fcfdb28 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 000000000000..52b69ebba3ec --- /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 000000000000..bb2a8f2050a9 --- /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 000000000000..e3e8a406db10 --- /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 bd91ae4e9340..e7441293d457 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 8a831375b412..7c1ad2949e91 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 756/846] 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 757/846] 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 93ce6bb85d8a..d82f970b8bf2 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 2e9900815d9a..d4d2f92a6a69 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 097aff4b1786..9d8fc863424c 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 6dc95fcfdb28..debd3c31d8bf 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 52b69ebba3ec..99d30360db1a 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 bb2a8f2050a9..5d03d9db1639 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 e3e8a406db10..03da118a0c42 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 e7441293d457..e366c75335c2 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 758/846] 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 9d8fc863424c..ced941fac1a4 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 99d30360db1a..a9cc8678c9d1 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 03da118a0c42..8f6835ed78d2 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 759/846] 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 ced941fac1a4..719a8b91f669 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 760/846] 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 719a8b91f669..ddb8cc25077e 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 debd3c31d8bf..6301d623a2b1 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 a9cc8678c9d1..a8736f1e6efe 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 5d03d9db1639..bede8cffc326 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 8f6835ed78d2..674f2952635f 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 761/846] 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 137a73630940..6c92e10522c9 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 344bb455aa53..ab8ff7117967 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 762/846] 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 c8576bcfcb44..8998fae09de3 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 c17a7cbc8d90..377e58e44174 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 f8f010b5e5c1..68a3c81b6a80 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 763/846] 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 d2a322e1223c..71a30d1c33d4 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 d92eb86fb2eb..e90ea0fc200a 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 764/846] 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 c7a3099b8ab0..e67aba19b7e8 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 fcf817b82c89..cd9363a85724 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 765/846] 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 96655e7f5b93..1ddcd18598de 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 cd9363a85724..d9471d251974 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 766/846] 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 9653e62cad07..3201adbf9a0b 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 cc635c2a202f..76417aa7ed09 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 8563b469a30d..6d7c5058b4f3 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 767/846] 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 9a948af8bfc4..716d027e434d 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 272b0900a31c..4e09d19ea214 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 5b80e6078eed..ad81b35a7664 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 fb89a9fce3d5..ba52fc2703f2 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 4a0c8620dc13..0d2115548fd2 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 8ff1e5dea600..474e62c30d50 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 768/846] 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 ddb8cc25077e..f3f1e31abde7 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 6301d623a2b1..b930d9aedcff 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 a8736f1e6efe..ceb8985d3d51 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 bede8cffc326..beca1de0ed16 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 674f2952635f..5d465666caf0 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 769/846] 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 ab8ff7117967..2572833b8deb 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 770/846] 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 1b73ced89b32..736730d4084f 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 8b0229125738..1bf3b810fb50 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 b1a4e06d4c32..d93433c607fb 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 9cb1cfb915d5..5bc45c87874b 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 771/846] 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 d82f970b8bf2..1bf25bcd0f96 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 d4d2f92a6a69..84ea2d5ca78c 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 63d7e3176b10..e50aa9ac15a2 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 6301d623a2b1..70369dc582a1 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 000000000000..8ca068293a61 --- /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 000000000000..57e9336a31fc --- /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 000000000000..90be1dc32837 --- /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 772/846] 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 3cc21809264f..8d54f75b5d19 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 f5d087fe1283..da2be6c8aa5a 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 773/846] 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 20613ac6afe9..272b0900a31c 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 21a63aec7a17..5b80e6078eed 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 000000000000..6b5b31a1a2ef --- /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 000000000000..fb9378108b98 --- /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 774/846] 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 d7043e7bd8f7..c8a5a9c94313 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 d482d466e449..6001ef37eb78 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 775/846] 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 000000000000..9a75911acbea --- /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 d4d2f92a6a69..697199861047 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 5e769c690a69..cd9b92efe583 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 000000000000..6e330f47dfc2 --- /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 000000000000..b114945504a2 --- /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 776/846] 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 6e330f47dfc2..8e30da85d141 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 777/846] update_lints --- CHANGELOG.md | 1 + src/lintlist/mod.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf2..287301772e28 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 6301d623a2b1..21185a08d5c3 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 778/846] 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 8e30da85d141..dd3adc494f1b 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 779/846] 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 dd3adc494f1b..88841597bb60 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 b114945504a2..b504f0454913 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 780/846] 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 697199861047..47cc0c903229 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 21185a08d5c3..63e9220ccd50 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 21bf49d16f04..315d5731b962 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 781/846] 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 367534499fd0..2000bdae363f 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 9a75911acbea..000000000000 --- 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 47cc0c903229..0c5baf96970c 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 63e9220ccd50..c955f37b83a3 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 0458950edee1..45aa3e64292e 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 315d5731b962..c8d49820c020 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 88841597bb60..000000000000 --- 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 b504f0454913..000000000000 --- 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 782/846] 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 2000bdae363f..1d201ff095b9 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 0c5baf96970c..c627df209763 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 c955f37b83a3..d9e019e2b611 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 783/846] 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 1d201ff095b9..fcebb54c6c21 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 c627df209763..6d00e313ce1b 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 784/846] 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 45aa3e64292e..000000000000 --- 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 c8d49820c020..000000000000 --- 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 000000000000..0458950edee1 --- /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 000000000000..21bf49d16f04 --- /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 000000000000..88841597bb60 --- /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 000000000000..b504f0454913 --- /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 785/846] Fix links to labeled issues --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 100c9edb3672..6494695606c3 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 786/846] 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 d82f970b8bf2..697e6166fdf8 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 d4d2f92a6a69..49ff8ad366e6 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 000000000000..48a050777b77 --- /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 6301d623a2b1..25a69e78c8f4 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 000000000000..bea62e1751e3 --- /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 787/846] 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 49ff8ad366e6..97e7cfd1bb26 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 48a050777b77..5443f1601fcb 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 bea62e1751e3..f4cfc92e1cd0 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 000000000000..2ac0fe98697e --- /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 788/846] 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 d82f970b8bf2..c9f0406a8069 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 d4d2f92a6a69..cc50655cb006 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 cc492917b9da..de54711d8511 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 6301d623a2b1..7bb68acc062f 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 000000000000..632a6592a28b --- /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 000000000000..6af0d034ef61 --- /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 000000000000..69b009eafc30 --- /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 789/846] 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 ceb8985d3d51..c784de0f6047 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 beca1de0ed16..df5f237c3fba 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 5d465666caf0..5bc01bf4e68e 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 790/846] 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 f3f1e31abde7..cc9ee28d0275 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 c784de0f6047..582f5c6f7a8e 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 df5f237c3fba..0e2b7ecadb31 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 5bc01bf4e68e..6603ab43437d 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 791/846] 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 716d027e434d..f4bb648d15a4 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 ba52fc2703f2..54853f4b8a26 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 0d2115548fd2..8da3d9477024 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 474e62c30d50..28fb9540afc0 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 792/846] 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 f4bb648d15a4..3a088709a7e4 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 54853f4b8a26..2a13469b1465 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 8da3d9477024..65ddd3c7fbfb 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 28fb9540afc0..26b23e315e3f 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 793/846] 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 2a13469b1465..5aeb0340b26f 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 65ddd3c7fbfb..0f249c23055e 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 26b23e315e3f..5100e9798c4e 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 794/846] 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 63d7e3176b10..bae129438693 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 84e31eaf2e9f..bb03ce403553 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 e44c58ea770a..fd3979f259a1 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 795/846] 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 3a088709a7e4..6a33aaaaab20 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 5aeb0340b26f..350da4965d11 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 0f249c23055e..ad2fb2e62892 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 5100e9798c4e..5a210fc89097 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 796/846] 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 dc5aa6691396..a799a644e970 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 912117647593..5a012ff4d27a 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 01ee35859f0d..c534bc25c20f 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 55d94b8257db..31522d4a5251 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 797/846] 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 cc9ee28d0275..22aa37e41fec 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 582f5c6f7a8e..5aa5a43cb92c 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 0e2b7ecadb31..df534031f54c 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 6603ab43437d..fc174c4c2705 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 798/846] 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 2eec51895f59..140676a5ffcf 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 799/846] 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 d82f970b8bf2..22f963981538 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 d4d2f92a6a69..1a950a7c334c 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 000000000000..28816c3076dd --- /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 e90ea0fc200a..000000000000 --- 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 dd2fd0bb445f..0ac8fff69f05 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 6301d623a2b1..f3536f263397 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 103ec27e7d75..a58e7e918e2f 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 000000000000..e4a2e9df4d7b --- /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 000000000000..5f42dcfb9b52 --- /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 800/846] 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 1d995cc9701b..ba080835f5c0 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 8d53b9799f0e..3a108bcfe6a4 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 000000000000..4f3108355ca7 --- /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 98ad994ea7b0..5e48757a4c50 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 000000000000..516d44bb7a9e --- /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 000000000000..426e0240830e --- /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 801/846] 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 4f3108355ca7..0680e578537c 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 516d44bb7a9e..9cd98e90c29d 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 426e0240830e..d521ebb56c43 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 802/846] 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 3a108bcfe6a4..648c65ef7b0c 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 803/846] 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 0680e578537c..ca3371a5d75c 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 804/846] 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 ca3371a5d75c..82ed2d6d69c3 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 000000000000..36bc52e3374e --- /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 9cd98e90c29d..ea60df5ae1bb 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 d521ebb56c43..21c1b3486cac 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 805/846] 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 648c65ef7b0c..9d88cc22d105 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 82ed2d6d69c3..df1cecb328cb 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 5e48757a4c50..25ede21da77c 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 806/846] 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 fd45a6da61ca..9c0efef95de8 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 44f2674eaa24..23ca35fffaaf 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 a9d26d48b127..8297b9d128dd 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 2fd6046ebcf5..8b327b2d4675 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 807/846] 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 7fd79deb56c6..c0dd7b352ade 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 b330b66776c1..5d6900f6b969 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 c54103b25c20..b41cfe32cfea 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 52d3c2c1daf0..d02603d7702c 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 4bf77dae6377..fc8efb81cfcd 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 859fad2f54d9..3430c137ec22 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 b6d9672a78e2..fe472e669493 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 c299b16c8ce8..2608638ff806 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 0b3af2d8bc35..c88848a40fc2 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 3f358d9ecaa0..4043d53299f6 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 1b2ff6bff048..fa4c3fe13e36 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 808/846] 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 b41cfe32cfea..813f3b6f3789 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 809/846] 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 c88848a40fc2..e321f2a1cef3 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 810/846] 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 813f3b6f3789..f51a0edc9c5c 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 811/846] 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 23ca35fffaaf..c3f75f283f49 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 0c1e61d74966..7b8e227f304c 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 812/846] 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 4cf03e820352..4f6e53577273 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 7b8e227f304c..fb807da5f8ab 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 813/846] 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 c0dd7b352ade..25f3b5da198a 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 5d6900f6b969..3be8bc0e36d6 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 f51a0edc9c5c..e8a741683dac 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 fc8efb81cfcd..6272ce45efbc 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 fe472e669493..13d108b56e02 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 2608638ff806..a72a74b9131d 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 e321f2a1cef3..64ceeb316d8e 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 fa4c3fe13e36..8645cabeabbb 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 814/846] Update triagebot.toml --- triagebot.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index ed3c83af616d..b7b20b40e68a 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 815/846] 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 22f963981538..869e9949167e 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 1ddcd18598de..dae3e03074fa 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 d9471d251974..167db15be200 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 000000000000..3b55336722c1 --- /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 1a950a7c334c..75629e13a8ec 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 da865a66c45f..c2b2236bc661 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 000000000000..261024caca76 --- /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 000000000000..98ddd9749516 --- /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 000000000000..f69d6bd5255d --- /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 816/846] 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 3b55336722c1..000000000000 --- 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 c9c4891bb08a..75e9c5c973b8 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 75629e13a8ec..3ad2e1e9d576 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 817/846] 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 dae3e03074fa..1ddcd18598de 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 167db15be200..d9471d251974 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 818/846] 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 75e9c5c973b8..9eba3750d2ab 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 819/846] 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 3ad2e1e9d576..8dff5ae42c3d 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 c2b2236bc661..695c7408e230 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 820/846] 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 9eba3750d2ab..542b4afcdf97 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 821/846] 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 542b4afcdf97..d0e7dea4a54a 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 822/846] 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 25f3b5da198a..287cf6bb725c 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 3be8bc0e36d6..138d1ab9b506 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 c0824bacbc73..5f9bed90845e 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 6272ce45efbc..d9b3e5c17f4f 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 000000000000..e66c9cc24207 --- /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 000000000000..6f08f4c3c535 --- /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 000000000000..8b06e13baa6b --- /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 823/846] 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 909e79f661a6..308e92057b75 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 0bbb9534928e..93303865e178 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 33605aca0199..b129d95c5602 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 59759f118933..73eb4ff7306f 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 19d69496709b..15cb933fedc9 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 42cac2ba4de2..1a493fbce0ef 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 295e2f35608b..6c36141a58c6 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 824/846] 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 c0824bacbc73..fc77cd52b8f9 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 825/846] 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 fc77cd52b8f9..d3e26a09ad43 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 01281ebaf6a6..bcedf11e5365 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 6fa2800e9bda..1975abb3e9fd 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 74d8533d4da8..2df1941aaaa4 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 826/846] 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 585944032e70..4e977051ab77 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 1975abb3e9fd..3fd4bdfd2b93 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 000000000000..cb2492a3be08 --- /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 000000000000..ed762dacd87c --- /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 000000000000..2cb76d70684d --- /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 827/846] 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 f5d051be198e..dbeb2354309c 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 bcedf11e5365..45427684d96e 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 000000000000..c3992d7e92cf --- /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 000000000000..447219a96839 --- /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 000000000000..6c2530e0379e --- /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 80dd2f744b3a..d93e5b114ecf 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 2df1941aaaa4..8a281c2dbd25 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 000000000000..ee7c1b0da6d9 --- /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 000000000000..6d0f1b7bd514 --- /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 000000000000..70e7c3dea545 --- /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 828/846] 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 4e977051ab77..87e16f5d09bd 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 3fd4bdfd2b93..96b9d6cc3c14 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 2cb76d70684d..000000000000 --- 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 cb2492a3be08..bd5b4f7165a4 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 ed762dacd87c..0b892caf20e8 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 000000000000..1837bc2ca3b8 --- /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 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 829/846] 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 6a33aaaaab20..45f3bc3ea858 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 1901de46ca89..b606f773cfba 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 b5db691f89f3..3eac32815be3 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 66d56fefc5b7ce22d2db65ee9dc1a5f9f6bf2f09 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 27 Oct 2020 07:42:13 +0200 Subject: [PATCH 830/846] 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 3be8bc0e36d6..7c8cb90fe1c8 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 bfe426a25eb8..6ca72d895c8d 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 8297b9d128dd..a1ecca0961a8 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 000000000000..01e28ae5e9d3 --- /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 000000000000..bd69d661b714 --- /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 831/846] 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 bf8e030cc294..c75efc6e99f8 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 69818b4d3c64..c1c08597ee67 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 cd9b92efe583..d5a0e0d1f294 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 832/846] 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 6494695606c3..abb82c469354 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 833/846] 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 c9e639e8728f..e4ad105c3513 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 834/846] 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 6c969c3ead0f..46ce92ea6d78 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 7c8cb90fe1c8..459fdd704439 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 d93433c607fb..4bdfca1a2921 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 a1ecca0961a8..0a8a4a5f9aed 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 6272ce45efbc..aba436e5c024 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 f3e19092480a..7f4ebf566733 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 fbae7c18b923..aee56dd4a5ef 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 4668f8565a65..c52e41c78894 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 e1401d2796a5..06d91497242e 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 26c65f32ad78..fccaf805616b 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 dace756c6fa8793badbc9b872047cc539952e718 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 28 Oct 2020 14:25:41 +0900 Subject: [PATCH 835/846] cargo dev update_lints --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f3b5da198a..d93efcafb198 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 836/846] Fix reference --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d93efcafb198..d10aefa2042e 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 837/846] 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 fc4dca5042b2..e9054c459edd 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 838/846] 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 d10aefa2042e..aa47930b520d 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 c5884361dff9..461c6e31d3eb 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 fb9493459455..2d3749698460 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 9e32fe36ece4..56755596c97f 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 d3400a7be09f..37b726fc00f1 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 839/846] 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 aa47930b520d..25f3b5da198a 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 840/846] 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 e9054c459edd..561954e7c3a2 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 841/846] 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 000000000000..c33580ff6ab6 --- /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 000000000000..8241dcd8feb7 --- /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 000000000000..6aa779aaeb3b --- /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 000000000000..9a7cf4b0919f --- /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 000000000000..2e3d9fd1e924 --- /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 000000000000..440973e24398 --- /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 000000000000..c19eca43884a --- /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 000000000000..95ebf23d8181 --- /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 000000000000..bd4a81d98e2e --- /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 000000000000..d973ea1e23a2 --- /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 000000000000..6f60d45d68a8 --- /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 000000000000..0e8353a418a8 --- /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 842/846] 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 c67efc10f157..9d9e836cc08a 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 843/846] 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 e84481f9b538..b1cdbab6de96 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 7b1b64f390a5..b74c93dc4a66 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 83e8a9cde3ff..20356afc3581 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 844/846] 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 d0e7dea4a54a..8e2f03d6e4e9 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 845/846] 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 abb82c469354..a8e2123656e9 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 fa0a78b130d52d4504eefbd16689b140e05fce59 Mon Sep 17 00:00:00 2001 From: henil Date: Fri, 30 Oct 2020 17:11:44 +0530 Subject: [PATCH 846/846] 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 b1cdbab6de96..9861d8cfc4e5 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 20356afc3581..add3b6b90fa2 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

)` 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 95fa28e1c0f7..d30b85d6a781 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 d69ae212414f..01281ebaf6a6 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 84a957a374c6..91718dd11755 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 f279850fef8a..aea3cc62afcc 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 feedc2f288a2..486de718bb56 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 b62080a073f3..6fa2800e9bda 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 2a0a43e83a65..74d8533d4da8 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 6f707987dbca..1cba29412b87 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 a6b7bcd63ff3..269cc13468bc 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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